Learn Rust With Entirely Too Many Linked Lists
45

An Introduction To Cursors

A cursor is a position inside a sequence — like the blinking insertion point in a text editor — that you can advance, retreat, and edit at. It is what Iterator is not: bidirectional, stable across mutations, and able to splice. The standard library exposes LinkedList::cursor_front_mut() (unstable) for exactly this.

  1. Why iterators don't fit. A &mut iterator hands out &mut T references whose lifetimes outlive each call to next. To insert or remove a node mid-iteration, the iterator would have to invalidate or reissue references, which the borrow checker doesn't allow. Cursors solve this by exposing &mut LinkedList<T> semantics at a single position rather than streaming references.
    // iterator: stream of references, can't edit around them
    for x in list.iter_mut() {
        *x += 1;
        // can't list.push_back(...) here — list is borrowed by iter
    }
    
    // cursor: one position, full mutation rights
    let mut c = list.cursor_front_mut();
    while let Some(x) = c.current() {
        if *x == 0 { c.remove_current(); } else { c.move_next(); }
    }
  2. The std model: CursorMut<'a, T> borrows &'a mut LinkedList<T> and tracks a position that can be either at a node or at a special "ghost" position between back and front (representing the gap that wraps around in a circular view). Movement past either end lands on the ghost; one more step lands back on the other end.
    // list:    A <-> B <-> C
    // states:  [G] A [.] B [.] C [G]   (G = ghost)
    //
    // move_next from C lands on ghost.
    // move_next from ghost lands on A.
    // current() returns None at the ghost.
    //
    // insert_after at ghost == push_front (effectively).
    // insert_before at ghost == push_back (effectively).