Learn Rust With Entirely Too Many Linked Lists
03

Ownership 101

Before we can write push and pop we have to talk about three kinds of self. This is the part of Rust where it stops looking like C++. References here are not T* — they're a checked relationship between a borrower and a value that the compiler tracks.

  1. self (no ref) consumes the receiver — like a value parameter that takes ownership. After the call, the caller no longer has the value. Use this for methods that intentionally destroy or transform the receiver.
    impl List {
        fn destroy(self) { /* self is owned, dropped at end */ }
    }
    
    let list = List::new();
    list.destroy();
    // list.destroy();   // error: value used after move
  2. &mut self is an exclusive borrow — for the duration of the call, no other reference to the value exists. This is the strict mutable-reference form. It's what you use for push, pop, clear, anything that mutates.
    impl List {
        fn clear(&mut self) { self.head = Link::Empty; }
    }
    
    let mut list = List::new();
    list.clear();    // ok
    list.clear();    // ok again — borrow ended at semicolon
  3. &self is a shared borrow — read-only, can coexist with other shared borrows but not with &mut. Use this for accessors and queries.
    impl List {
        fn is_empty(&self) -> bool {
            matches!(self.head, Link::Empty)
        }
    }