Learn Rust With Entirely Too Many Linked Lists
05

Pop

Pop is push run backwards: take the head node, hand the caller its element, splice the rest of the list back into self.head. The empty case has to return something, which is what Option<T> is for.

  1. First instinct: match &self.head. Empty → return None. More → return the element. The catch: &self.head is a borrow, which prevents you from also assigning to self.head inside the same arm.
    pub fn pop(&mut self) -> Option<i32> {
        match &self.head {
            Link::Empty => None,
            Link::More(node) => {
                // we want node.elem AND to set self.head = node.next,
                // but the borrow on self.head is still live — won't compile.
                Some(node.elem)
            }
        }
    }
  2. Same trick as push: mem::replace to take the head out by value, leaving Link::Empty behind. Now we own the old head and can disassemble it without fighting borrows.
    use std::mem;
    
    pub fn pop(&mut self) -> Option<i32> {
        match mem::replace(&mut self.head, Link::Empty) {
            Link::Empty => None,
            Link::More(node) => {
                self.head = node.next;
                Some(node.elem)
            }
        }
    }
  3. When node falls out of scope at the end of the arm, the Box<Node> is dropped and the heap allocation is freed. The element was already moved out into the return value, so there's no double-free or leak.
    let mut list = List::new();
    list.push(1);
    list.push(2);
    assert_eq!(list.pop(), Some(2));
    assert_eq!(list.pop(), Some(1));
    assert_eq!(list.pop(), None);