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.
- Definition and constructors.
cursor_mutstarts at the ghost;cursor_front_mutstarts at the front (or the ghost if empty). The'alifetime 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 } } } -
move_nextandmove_prev. From a real node, advance to the node'sback(orfront); if that'sNone, land on the ghost. From the ghost, wrap tolist.front(orlist.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) } } } -
split_beforecuts the list at the cursor: the prefix [front .. cur) is detached and returned as a freshLinkedList<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 mirrorsplit_afterworks 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()) } } } -
splice_beforeinserts an entireLinkedList<T>immediately before the cursor's position. We rewire four pointers (the input list's front and back, plus the surrounding nodes), updatelen, and zero out the input list's pointers so itsDropis a no-op. The mirrorsplice_afteris 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); } } }