23
Breaking
pop_front reverses push_front: take the head, splice the second node in as the new head, return the old element. The complication is unwrapping the element out from inside Rc<RefCell<Node<T>>> — there are three layers to peel.
- Take the head out with
Option::take. Now we own oneRc<RefCell<Node<T>>>. Look at the head'snextfield — that's the new head. If it exists, null itsprev; either way, splice it intoself.head. IfnextisNone, the list is now empty, so clearself.tailtoo.pub fn pop_front(&mut self) -> Option<T> { self.head.take().map(|old_head| { match old_head.borrow_mut().next.take() { Some(new_head) => { new_head.borrow_mut().prev.take(); self.head = Some(new_head); } None => { self.tail.take(); } } // ... extract elem from old_head ... Rc::try_unwrap(old_head).ok().unwrap().into_inner().elem }) } -
Rc::try_unwrap(rc)returnsOk(T)if the refcount is 1 (we are the unique owner) andErr(rc)otherwise — it can't safely move out if anyone else still holds a handle. Once we've severed the neighbours' pointers above, ourold_headis the lastRc, sotry_unwrapsucceeds. ThenRefCell::into_innerextracts theNode<T>from the cell, and.elemfinally gets us the element.// the unwrap chain, expanded: let rc: Rc<RefCell<Node<T>>> = old_head; let cell: RefCell<Node<T>> = Rc::try_unwrap(rc).ok().unwrap(); let node: Node<T> = cell.into_inner(); let elem: T = node.elem; - Why
.ok().unwrap()rather than.unwrap()directly?Rc::try_unwrapreturnsResult<T, Rc<T>>— the error carries the Rc back, which doesn't implementDebugunlessTdoes..ok()discards the error into anOption, whichunwrapis happy with. Theunwraphere would only fire on a logic bug; in correct code the refcount is always 1 at this point.let mut list = List::new(); list.push_front(1); list.push_front(2); assert_eq!(list.pop_front(), Some(2)); assert_eq!(list.pop_front(), Some(1)); assert_eq!(list.pop_front(), None);