Learn Rust With Entirely Too Many Linked Lists
48

Final Code

The complete module: LinkedList<T>, the trait suite, three iterators, CursorMut, the lot. It's the same shape as std::collections::LinkedList and passes Miri. It's also a linked list, which means it's almost always the wrong choice in production.

  1. The complete module — keep it open while reading the postscript below.
    use std::cmp::Ordering;
    use std::fmt;
    use std::hash::{Hash, Hasher};
    use std::marker::PhantomData;
    use std::ptr::NonNull;
    
    pub struct LinkedList<T> {
        front: Option<NonNull<Node<T>>>,
        back: Option<NonNull<Node<T>>>,
        len: usize,
        _marker: PhantomData<Box<Node<T>>>,
    }
    
    struct Node<T> {
        front: Option<NonNull<Node<T>>>,
        back: Option<NonNull<Node<T>>>,
        elem: T,
    }
    
    impl<T> LinkedList<T> {
        pub const fn new() -> Self {
            LinkedList { front: None, back: None, len: 0, _marker: PhantomData }
        }
        pub fn len(&self) -> usize { self.len }
        pub fn is_empty(&self) -> bool { self.len == 0 }
        pub fn clear(&mut self) { while self.pop_front().is_some() {} }
    
        pub fn push_front(&mut self, elem: T) { /* ... */ }
        pub fn pop_front(&mut self) -> Option<T> { /* ... */ }
        pub fn push_back(&mut self, elem: T) { /* ... */ }
        pub fn pop_back(&mut self) -> Option<T> { /* ... */ }
    
        pub fn front(&self) -> Option<&T> { /* ... */ unimplemented!() }
        pub fn front_mut(&mut self) -> Option<&mut T> { unimplemented!() }
        pub fn back(&self) -> Option<&T> { unimplemented!() }
        pub fn back_mut(&mut self) -> Option<&mut T> { unimplemented!() }
    
        pub fn iter(&self) -> Iter<'_, T> { unimplemented!() }
        pub fn iter_mut(&mut self) -> IterMut<'_, T> { unimplemented!() }
        pub fn cursor_mut(&mut self) -> CursorMut<'_, T> { unimplemented!() }
    }
    
    impl<T> Default for LinkedList<T> {
        fn default() -> Self { LinkedList::new() }
    }
    impl<T> Drop for LinkedList<T> {
        fn drop(&mut self) { while self.pop_front().is_some() {} }
    }
    
    unsafe impl<T: Send> Send for LinkedList<T> {}
    unsafe impl<T: Sync> Sync for LinkedList<T> {}
    
    // ... Iter, IterMut, IntoIter, CursorMut, Clone, Extend, FromIterator,
    // Debug, PartialEq, Eq, PartialOrd, Ord, Hash, plus their Send/Sync impls,
    // fill out the rest of the module exactly as shown across the chapter.
  2. What's still wrong: linked lists thrash the cache (every node is its own allocation), pressure the allocator (one malloc per push), waste memory (two pointers of overhead per element), and don't vectorize (no contiguous storage). For nearly every workload, Vec<T> or VecDeque<T> outperforms a linked list by an order of magnitude. The places linked lists genuinely win — O(1) splice of arbitrary sublists, intrusive lists with hand-managed nodes — are rare in application code.
    // the part where we admit defeat:
    // for x in 0..1_000_000 { list.push_back(x); }    // a million mallocs
    // for x in 0..1_000_000 { vec.push(x); }          // ~20 mallocs total