Mastering Linked Lists in Rust: Beyond the Basics

Murat Aslan
3 min readMay 11, 2024

A Developer tackling medium-sized projects, you’re familiar with the strengths of various data structures. While arrays excel at random access, Linked Lists offer a unique approach — a dynamic chain of nodes well-suited for specific use cases. This article delves into Linked Lists in Rust, exploring their intricacies, practical applications, and real-world coding examples to empower you to leverage them effectively in your Rust projects.

Unveiling the Linked List: A Chain Reaction of Nodes

A Linked List is a linear data structure where data resides in elements called nodes. Unlike arrays, which store elements contiguously in memory, Linked Lists rely on pointers or references for navigation. Each node typically contains two key components:

  • Value: The actual data element stored in the node (e.g., a string, an integer, or even a custom struct).
  • Next Pointer/Reference: A reference to the next node in the sequence. This pointer chain allows you to traverse the list and access elements.

Singly vs. Doubly Linked Lists:

Rust’s std::collections::LinkedList implementation is a doubly-linked list. In this structure, each node has two pointers: one pointing to the next node and another pointing to the previous node. This enables efficient navigation in both directions. Singly-linked lists only have a pointer to the next node, offering a simpler structure but limiting navigation options.

Building Your Node Chain: Crafting a Linked List

While Rust doesn’t provide a built-in singly-linked list, you can create one using custom structs and pointers. Here’s a simplified example of a Node struct for a singly-linked list:

struct Node<T> {
value: T,
next: Option<Box<Node<T>>>, // Optional pointer to the next node (can be None if it's the last node)
}

However, for most use cases in Rust, you’ll likely leverage the LinkedList type from the standard library:

use std::collections::LinkedList;

let mut names_list: LinkedList<String> = LinkedList::new(); // Empty linked list for names

This code creates an empty LinkedList named names_list to store strings.

Interacting with Linked Lists: Adding, Removing, and Traversing

While Linked Lists excel at dynamic insertions and removals, random access by index is inefficient. Here’s a breakdown of key operations:

  • push_front(element): Adds a new element to the front of the list.
  • push_back(element): Adds a new element to the back of the list.
  • pop_front(): Removes and returns the element from the front of the list.
  • pop_back(): Removes and returns the element from the back of the list.
  • iter(): Returns an iterator that allows you to loop through all elements in the list, one by one.

Traversing a Linked List: Due to the pointer-based structure, iterating through a Linked List requires following the chain of pointers from one node to the next. This can be less efficient than random access in arrays, but for frequent insertions and deletions, Linked Lists can be advantageous.

Extra Caution: Ownership and Borrowing Considerations: When working with custom linked list implementations, be mindful of Rust’s ownership and borrowing rules to avoid memory-related errors.

Linked Lists in Action:

While not a one-size-fits-all solution, Linked Lists offer valuable functionalities in specific scenarios within your medium-sized Rust projects:

  • Implementing Stacks and Queues: Linked Lists are a natural fit for building stacks (LIFO — Last In, First Out) and queues (FIFO — First In, First Out) data structures. Their efficient insertions and removals at the front or back make them ideal for these use cases.
  • Managing Dynamic Data: When dealing with data of varying sizes or where frequent insertions and removals are expected, Linked Lists can adapt efficiently without the memory reallocation challenges faced by arrays.
  • Custom Data Structures: Linked Lists can be used as building blocks for more complex data structures like hash tables or graphs, where dynamic node creation and manipulation are crucial.

Here’s a code example showcasing a LinkedList used as a simple undo/redo mechanism in a text editor:

use std::collections::LinkedList;

struct TextEditor {
buffer: LinkedList<String>, // List of lines in the editor
undo_stack: LinkedList<String>, // Stack of previous states for undo
}

impl TextEditor {
fn insert_char(&

--

--