Linked List in Rust

Linked List is a data structure which is optimal to store an arbitrary list of ordered items, where the items are added and removed from the list frequently.

The linked list that we are going to implement is called Singly Linked List, when given a node we can go to the next node, but we cannot go back to the previous node.

How to define Linked List?

To answer that we need to answer the following questions,

  • How to represent the items?

  • How to link the items together?

  • How to define the starting point of the linked list?

How to represent the items?

Rust supports generic programming, since the items are arbitrary in nature let's denote it using the generic variable T.

The generic type T that we have chosen above does not have any information about the linked list that we are trying to build. So let's define a new type,

struct Node<T> {
    val: T,
    next: Node<T>,
}

In the above definition the next field will point to the next node.

Are we good to go? NO! The rust compiler is not happy about the fact that it cannot deduce the size of the Node type at compile time. You may wonder why that is the case, as a definition similar to this will succeed in programming languages like Java, C#, Scala, Swift etc., Because those languages by default will store every value of type Node in the heap and the Node::next field will only hold the address of the value stored in the heap.

Contrary to the typical garbage collected languages, rust tries to put every value in the stack first. It tries to compute how much size is required to store the entire linked list in the stack and that could be infinite.

So as explained in the previous chapter, Box is used to solve the problem as follows,

struct Node<T> {
    val: T,
    next: Box<Node<T>>,
}

Are we done yet? No! How to represent the case that there is no next node? If you remember correctly Box cannot represent null value, so we need Option to represent the absence of the next node.

struct Node<T> {
    val: T,
    next: Option<Box<Node<T>>>,
}

How to define the starting point of the linked list?

This one is pretty simple,

struct LinkedList<T> {
    head: Option<Node<T>>,
}

Creating the list

Here are two ways we can define an Empty List

fn main() {
    // Create an empty linked list of type u32
    let empty_list: LinkedList<u32> = LinkedList { head: Option::None }
    println!("Created an empty linked list!");
}
// Or by implementing a constructor which returns a Linked List with an Option::None header
impl<T> LinkedList<T> {
    fn new() -> Self {
        LinkedList { head: Option::None }
    }
}

fn main() {
    // Create an empty linked list of type u32
    let list: LinkedList<u32> = LinkedList::new();
    println!("Created an empty linked list!");
}

Defining a Singleton List

fn main() {
    // Create a node with the value 42
    let node = Node {
        value: 42,
        next: None,
    };

    // Create a singleton linked list by setting head to the node
    let singleton_list = LinkedList {
        head: Some(node),
    };
    println!("Created a singleton linked list!");
}

Defining a List with 2 elements

fn main() {
    // Create the second node with the value 24, we need to create it first so we can 
    // reference it as the next value in the first node
    let second_node = Node {
        value: 24,
        next: None,
    };
    // Create the first node with the value 42 and next pointing to the second node
    let first_node = Node {
        value: 42,
        next: Some(Box::new(second_node)),
    };

    // Create a linked list by setting head to the first node
    let two_elements_list = LinkedList {
        head: Some(first_node),
    };
    println!("Created a linked list with 2 elements!");
}

With the above definition the first item, will be stored in the stack and rest of them will be stored in the heap. Pay close attention to the type of LinkedList::head.

Last updated