Learn Rust With Entirely Too Many Linked Lists
36

Final Code

The whole module in one block. The cost we paid for O(1) push_back: a raw-pointer layout, hand-written Drop, and the obligation to think about stacked borrows on every modification forever. The benefit: one allocation per push, no Box clone of the tail, no shared-ownership overhead. For a queue, it's the right trade.

  1. The complete unsafe queue. Keep this on hand: in chapter 6 we generalize to a doubly-linked deque, where stacked borrows gets even fussier and we'll switch from raw pointers to NonNull<T> plus the same disciplines.
    use std::marker::PhantomData;
    use std::ptr;
    
    pub struct List<T> {
        head: *mut Node<T>,
        tail: *mut Node<T>,
    }
    
    struct Node<T> {
        elem: T,
        next: *mut Node<T>,
    }
    
    pub struct Iter<'a, T> {
        next: *const Node<T>,
        _marker: PhantomData<&'a T>,
    }
    
    pub struct IntoIter<T>(List<T>);
    
    impl<T> List<T> {
        pub fn new() -> Self {
            List { head: ptr::null_mut(), tail: ptr::null_mut() }
        }
    
        pub fn push_back(&mut self, elem: T) {
            unsafe {
                let new_tail = Box::into_raw(Box::new(Node {
                    elem,
                    next: ptr::null_mut(),
                }));
                if self.tail.is_null() {
                    self.head = new_tail;
                } else {
                    (*self.tail).next = new_tail;
                }
                self.tail = new_tail;
            }
        }
    
        pub fn pop_front(&mut self) -> Option<T> {
            unsafe {
                if self.head.is_null() {
                    None
                } else {
                    let head_box: Box<Node<T>> = Box::from_raw(self.head);
                    self.head = head_box.next;
                    if self.head.is_null() {
                        self.tail = ptr::null_mut();
                    }
                    Some(head_box.elem)
                }
            }
        }
    
        pub fn peek_front(&self) -> Option<&T> {
            unsafe { self.head.as_ref().map(|n| &n.elem) }
        }
    
        pub fn peek_back(&self) -> Option<&T> {
            unsafe { self.tail.as_ref().map(|n| &n.elem) }
        }
    
        pub fn is_empty(&self) -> bool {
            self.head.is_null()
        }
    
        pub fn iter(&self) -> Iter<'_, T> {
            Iter { next: self.head, _marker: PhantomData }
        }
    
        pub fn into_iter(self) -> IntoIter<T> { IntoIter(self) }
    }
    
    impl<'a, T> Iterator for Iter<'a, T> {
        type Item = &'a T;
        fn next(&mut self) -> Option<&'a T> {
            unsafe {
                self.next.as_ref().map(|node| {
                    self.next = node.next;
                    &node.elem
                })
            }
        }
    }
    
    impl<T> Iterator for IntoIter<T> {
        type Item = T;
        fn next(&mut self) -> Option<T> { self.0.pop_front() }
    }
    
    impl<T> Drop for List<T> {
        fn drop(&mut self) {
            while self.pop_front().is_some() {}
        }
    }