Learn Rust With Entirely Too Many Linked Lists
46

Implementing Cursors

CursorMut<'a, T> holds &'a mut LinkedList<T> and cur: Option<NonNull<Node<T>>> for the current position (None is the ghost). We also track an index: Option<usize> so callers can ask where they are without walking. Methods: movement, peeking, insertion, removal, and the splice/split family.

  1. Definition and constructors. cursor_mut starts at the ghost; cursor_front_mut starts at the front (or the ghost if empty). The 'a lifetime ties the cursor to the borrow of the list.
    pub struct CursorMut<'a, T> {
        list: &'a mut LinkedList<T>,
        cur: Option<NonNull<Node<T>>>,
        index: Option<usize>,
    }
    
    impl<T> LinkedList<T> {
        pub fn cursor_mut(&mut self) -> CursorMut<'_, T> {
            CursorMut { list: self, cur: None, index: None }
        }
    }
  2. move_next and move_prev. From a real node, advance to the node's back (or front); if that's None, land on the ghost. From the ghost, wrap to list.front (or list.back). Index updates accordingly.
    impl<'a, T> CursorMut<'a, T> {
        pub fn move_next(&mut self) {
            match self.cur {
                Some(n) => unsafe {
                    self.cur = (*n.as_ptr()).back;
                    self.index = if self.cur.is_some() {
                        Some(self.index.unwrap() + 1)
                    } else { None };
                },
                None => {
                    self.cur = self.list.front;
                    self.index = if self.cur.is_some() { Some(0) } else { None };
                }
            }
        }
    
        pub fn current(&mut self) -> Option<&mut T> {
            unsafe { self.cur.map(|n| &mut (*n.as_ptr()).elem) }
        }
    
        pub fn peek_next(&mut self) -> Option<&mut T> {
            let next = match self.cur {
                Some(n) => unsafe { (*n.as_ptr()).back },
                None => self.list.front,
            };
            unsafe { next.map(|n| &mut (*n.as_ptr()).elem) }
        }
    }
  3. split_before cuts the list at the cursor: the prefix [front .. cur) is detached and returned as a fresh LinkedList<T>, and the cursor's list becomes [cur .. back]. The cursor stays on the same node; the returned list takes the front pointer and a recomputed length. The mirror split_after works the other direction.
    impl<'a, T> CursorMut<'a, T> {
        pub fn split_before(&mut self) -> LinkedList<T> {
            if let Some(cur) = self.cur {
                let old_idx = self.index.unwrap();
                unsafe {
                    let prev = (*cur.as_ptr()).front.take();
                    let new_front = self.list.front;
                    let new_back = prev;
                    if let Some(p) = prev { (*p.as_ptr()).back = None; }
                    self.list.front = Some(cur);
                    let prefix_len = old_idx;
                    self.list.len -= prefix_len;
                    self.index = Some(0);
                    LinkedList {
                        front: new_front,
                        back: new_back,
                        len: prefix_len,
                        _marker: PhantomData,
                    }
                }
            } else {
                // ghost: take the whole list
                std::mem::replace(self.list, LinkedList::new())
            }
        }
    }
  4. splice_before inserts an entire LinkedList<T> immediately before the cursor's position. We rewire four pointers (the input list's front and back, plus the surrounding nodes), update len, and zero out the input list's pointers so its Drop is a no-op. The mirror splice_after is the same pattern.
    impl<'a, T> CursorMut<'a, T> {
        pub fn splice_before(&mut self, mut other: LinkedList<T>) {
            if other.len == 0 { return; }
            let other_front = other.front.take().unwrap();
            let other_back = other.back.take().unwrap();
            let n = other.len;
            other.len = 0;
            unsafe {
                match self.cur {
                    Some(cur) => match (*cur.as_ptr()).front {
                        Some(prev) => {
                            (*prev.as_ptr()).back = Some(other_front);
                            (*other_front.as_ptr()).front = Some(prev);
                            (*other_back.as_ptr()).back = Some(cur);
                            (*cur.as_ptr()).front = Some(other_back);
                        }
                        None => {
                            (*other_back.as_ptr()).back = Some(cur);
                            (*cur.as_ptr()).front = Some(other_back);
                            self.list.front = Some(other_front);
                        }
                    },
                    None => {
                        // ghost: append to back
                        if let Some(b) = self.list.back {
                            (*b.as_ptr()).back = Some(other_front);
                            (*other_front.as_ptr()).front = Some(b);
                        } else {
                            self.list.front = Some(other_front);
                        }
                        self.list.back = Some(other_back);
                    }
                }
            }
            self.list.len += n;
            if let Some(i) = self.index { self.index = Some(i + n); }
        }
    }