Intermediate Projects

Generic Linked List

Implementing a full-featured generic singly linked list (LinkedList<T>) with $O(1)$ push_front, pop_front, peek, peek_mut, all three standard iterator modes (IntoIter<T>, Iter<'a, T>, IterMut<'a, T>), FromIterator<T>, and an iterative Drop implementation brings together ownership, lifetime elision, and Option::as_deref / Option::take idioms in 100% safe Rust.

Generic `LinkedList<T>` Ownership & Iterator Modes:

  `LinkedList<T>`
    head ---> Box<Node<T>> { elem: T, next: Option<Box<Node<T>>> }
      |
      +-- `into_iter(self)` ---> `IntoIter<T>`       (Pops owned `T` via `pop_front`)
      +-- `iter(&self)`     ---> `Iter<'a, T>`       (Advances `next: Option<&'a Node<T>>`)
      +-- `iter_mut(&mut)`  ---> `IterMut<'a, T>`    (Advances `next: Option<&'a mut Node<T>>`)

Key Idioms Used in Safe LinkedList<T>

Operation Idiom Why It Works Without unsafe
push_front / pop_front self.head.take() Replaces self.head with None and yields the owned Option<Box<Node<T>>> in $O(1)$.
Iter<'a, T>::next self.next.map(|node| { self.next = node.next.as_deref(); &node.elem }) Option::as_deref() converts &Option<Box<Node<T>>> directly into Option<&Node<T>>!
IterMut<'a, T>::next self.next.take().map(|node| { self.next = node.next.as_deref_mut(); &mut node.elem }) Because &mut T is not Copy, self.next.take() extracts the current Option<&'a mut Node<T>> before advancing to node.next.as_deref_mut()!
Iterative Drop while let Some(mut node) = cur { cur = node.next.take(); } Unlinks each Box<Node<T>> iteratively in $O(1)$ stack space so dropping 1,000,000 nodes never overflows the call stack.

Complete Runnable Implementation

use std::iter::FromIterator;

pub struct LinkedList<T> {
    head: Option<Box<Node<T>>>,
    len: usize,
}

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

impl<T> Default for LinkedList<T> {
    fn default() -> Self {
        Self::new()
    }
}

impl<T> LinkedList<T> {
    pub const fn new() -> Self {
        Self { head: None, len: 0 }
    }

    pub const fn len(&self) -> usize {
        self.len
    }

    pub const fn is_empty(&self) -> bool {
        self.len == 0
    }

    pub fn push_front(&mut self, elem: T) {
        self.head = Some(Box::new(Node {
            elem,
            next: self.head.take(),
        }));
        self.len += 1;
    }

    pub fn pop_front(&mut self) -> Option<T> {
        self.head.take().map(|node| {
            self.head = node.next;
            self.len -= 1;
            node.elem
        })
    }

    pub fn peek(&self) -> Option<&T> {
        self.head.as_ref().map(|node| &node.elem)
    }

    pub fn peek_mut(&mut self) -> Option<&mut T> {
        self.head.as_mut().map(|node| &mut node.elem)
    }

    /// Reverses the linked list in-place in O(n) time and O(1) auxiliary memory!
    pub fn reverse(&mut self) {
        let mut prev = None;
        let mut current = self.head.take();
        while let Some(mut boxed_node) = current {
            let next = boxed_node.next.take();
            boxed_node.next = prev;
            prev = Some(boxed_node);
            current = next;
        }
        self.head = prev;
    }

    pub fn iter(&self) -> Iter<'_, T> {
        Iter {
            next: self.head.as_deref(),
        }
    }

    pub fn iter_mut(&mut self) -> IterMut<'_, T> {
        IterMut {
            next: self.head.as_deref_mut(),
        }
    }
}

impl<T> Drop for LinkedList<T> {
    fn drop(&mut self) {
        let mut cur = self.head.take();
        while let Some(mut boxed) = cur {
            cur = boxed.next.take();
        }
    }
}

pub struct IntoIter<T>(LinkedList<T>);

impl<T> Iterator for IntoIter<T> {
    type Item = T;
    fn next(&mut self) -> Option<Self::Item> {
        self.0.pop_front()
    }
}

pub struct Iter<'a, T> {
    next: Option<&'a Node<T>>,
}

impl<'a, T> Iterator for Iter<'a, T> {
    type Item = &'a T;
    fn next(&mut self) -> Option<Self::Item> {
        let node = self.next?;
        self.next = node.next.as_deref();
        Some(&node.elem)
    }
}

pub struct IterMut<'a, T> {
    next: Option<&'a mut Node<T>>,
}

impl<'a, T> Iterator for IterMut<'a, T> {
    type Item = &'a mut T;
    fn next(&mut self) -> Option<Self::Item> {
        let node = self.next.take()?;
        self.next = node.next.as_deref_mut();
        Some(&mut node.elem)
    }
}

impl<T> IntoIterator for LinkedList<T> {
    type Item = T;
    type IntoIter = IntoIter<T>;
    fn into_iter(self) -> Self::IntoIter {
        IntoIter(self)
    }
}

impl<'a, T> IntoIterator for &'a LinkedList<T> {
    type Item = &'a T;
    type IntoIter = Iter<'a, T>;
    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}

impl<'a, T> IntoIterator for &'a mut LinkedList<T> {
    type Item = &'a mut T;
    type IntoIter = IterMut<'a, T>;
    fn into_iter(self) -> Self::IntoIter {
        self.iter_mut()
    }
}

impl<T> FromIterator<T> for LinkedList<T> {
    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
        let mut list = Self::new();
        for item in iter {
            list.push_front(item);
        }
        list.reverse();
        list
    }
}

fn main() {
    let mut list: LinkedList<i32> = [10, 20, 30].into_iter().collect();
    assert_eq!(list.len(), 3);

    // Mutate all elements in-place via `IterMut`:
    for val in &mut list {
        *val += 5;
    }

    // Reverse in-place in O(1) memory:
    list.reverse();

    let collected: Vec<i32> = list.into_iter().collect();
    assert_eq!(collected, vec![35, 25, 15]);
    println!("Generic LinkedList after +5 and in-place reverse: {collected:?}");
}
Generic LinkedList after +5 and in-place reverse: [35, 25, 15]