Learn Rust With Entirely Too Many Linked Lists
24

Peek

Peek wants to return Option<&T> — a borrow of the head element without taking it. We can't. The element lives inside a RefCell, and the only way to read across a RefCell is through a Ref<T> guard. The RefCell type leaks into our public API.

  1. RefCell::borrow() returns a Ref<'_, T> — a smart pointer that derefs to &T and bumps an internal read-borrow counter. The counter decrements on drop. Returning &T directly out of a borrow() would let the caller hold the reference past the guard's lifetime, which would let you violate the borrow rules. The API forces you to keep the guard.
    use std::cell::Ref;
    
    pub fn peek_front(&self) -> Option<Ref<T>> {
        self.head.as_ref().map(|node| {
            Ref::map(node.borrow(), |n| &n.elem)
        })
    }
  2. Ref::map projects the guard. It takes Ref<'_, A> and a closure &A -> &B, returns Ref<'_, B> — same guard, narrower view. We use it to convert Ref<Node<T>> into Ref<T> so callers don't see the Node type at all. They still see Ref, though.
    let mut list = List::new();
    list.push_front("hello");
    {
        let head: Ref<&str> = list.peek_front().unwrap();
        assert_eq!(*head, "hello");
    } // guard drops here, RefCell is unborrowed
  3. This is where the design admits defeat. Option<Ref<T>> in the public signature means callers must import std::cell::Ref, must understand RefCell semantics, and must be careful not to hold the guard across calls that would re-borrow the same cell. We are exporting our internal storage choice as part of our type signature.
    // The leak — compare:
    // std::collections::VecDeque::front(&self) -> Option<&T>
    // our List::peek_front(&self)              -> Option<Ref<T>>