Learn Rust With Entirely Too Many Linked Lists
16

Layout

Persistent lists share suffixes. List B = 1 -> 2 -> 3 and list C = 0 -> 2 -> 3 should both point at the same 2 -> 3 tail in memory; freeing B must not free a node C is still using. Box<T> is a single owner, so it can't model this. Rust's answer is Rc<T> — a shared owner with an embedded refcount, the safe-Rust analogue of std::shared_ptr.

  1. Rc<T> (reference-counted) is a heap allocation with a count next to the value. Rc::clone bumps the count; the Drop of an Rc decrements it; the last drop frees the allocation. Conceptually identical to std::shared_ptr<T> minus the atomics — single-threaded, so the count is a plain integer.
    use std::rc::Rc;
    
    pub struct List<T> {
        head: Link<T>,
    }
    
    type Link<T> = Option<Rc<Node<T>>>;
    
    struct Node<T> {
        elem: T,
        next: Link<T>,
    }
  2. Two lists can now point at the same Rc<Node<T>>. Clone the Rc and the count goes from 1 to 2; both lists hold a handle; whichever drops second runs the destructor. We're using Option<Rc<Node<T>>> rather than a hand-rolled Link enum — last chapter taught us that.
    // list_a:  1 -> 2 -> 3 -> Nil
    // list_b:  0 -> 2 -> 3 -> Nil   (shares the 2->3 tail)
    //
    // the node holding `2`:
    //   refcount = 2  (list_a's node-1 points at it; list_b's node-0 points at it)
    // the node holding `3`:
    //   refcount = 1  (only the node holding `2` points at it)
  3. One catch: Rc<T> only hands out shared references. There is no Rc::get_mut once the count is above one, because mutation through a shared owner would be a data race waiting to happen. Persistence is the natural fit — we never mutate; we build new lists that share old tails. (For interior mutability under Rc you'd reach for RefCell, which is the next chapter's mistake.)
    let shared: Rc<i32> = Rc::new(42);
    let a = Rc::clone(&shared);  // refcount = 2
    let b = Rc::clone(&shared);  // refcount = 3
    
    // *a = 99;  // error: cannot assign through `&` reference
    //           // Rc only gives out shared access.