27
Final Code
Here's the whole module. It compiles, it passes basic tests, and you would never use it. The borrow checks are runtime, the error mode is panic, the API leaks Ref<T>, and Iter/IterMut are unimplementable. Chapter 5 starts over with raw pointers.
- All in one place. Note what you don't see: no
unsafe, no raw pointers, no manualDrop. The Rc graph cleans itself up because a doubly-linked list has no cycles between its endpoints — when the list drops, the head Rc decrements, which drops the next link, which decrements the next Rc, and so on.use std::cell::{Ref, RefCell, RefMut}; use std::rc::Rc; pub struct List<T> { head: Link<T>, tail: Link<T>, } type Link<T> = Option<Rc<RefCell<Node<T>>>>; struct Node<T> { elem: T, next: Link<T>, prev: Link<T>, } impl<T> List<T> { pub fn new() -> Self { List { head: None, tail: None } } pub fn push_front(&mut self, elem: T) { let new_head = Rc::new(RefCell::new(Node { elem, prev: None, next: None })); match self.head.take() { Some(old_head) => { old_head.borrow_mut().prev = Some(new_head.clone()); new_head.borrow_mut().next = Some(old_head); self.head = Some(new_head); } None => { self.tail = Some(new_head.clone()); self.head = Some(new_head); } } } 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(); } } Rc::try_unwrap(old_head).ok().unwrap().into_inner().elem }) } pub fn push_back(&mut self, elem: T) { let new_tail = Rc::new(RefCell::new(Node { elem, prev: None, next: None })); match self.tail.take() { Some(old_tail) => { old_tail.borrow_mut().next = Some(new_tail.clone()); new_tail.borrow_mut().prev = Some(old_tail); self.tail = Some(new_tail); } None => { self.head = Some(new_tail.clone()); self.tail = Some(new_tail); } } } pub fn pop_back(&mut self) -> Option<T> { self.tail.take().map(|old_tail| { match old_tail.borrow_mut().prev.take() { Some(new_tail) => { new_tail.borrow_mut().next.take(); self.tail = Some(new_tail); } None => { self.head.take(); } } Rc::try_unwrap(old_tail).ok().unwrap().into_inner().elem }) } pub fn peek_front(&self) -> Option<Ref<T>> { self.head.as_ref().map(|n| Ref::map(n.borrow(), |n| &n.elem)) } pub fn peek_back(&self) -> Option<Ref<T>> { self.tail.as_ref().map(|n| Ref::map(n.borrow(), |n| &n.elem)) } pub fn peek_front_mut(&mut self) -> Option<RefMut<T>> { self.head.as_ref().map(|n| RefMut::map(n.borrow_mut(), |n| &mut n.elem)) } pub fn peek_back_mut(&mut self) -> Option<RefMut<T>> { self.tail.as_ref().map(|n| RefMut::map(n.borrow_mut(), |n| &mut n.elem)) } } pub struct IntoIter<T>(List<T>); impl<T> List<T> { pub fn into_iter(self) -> IntoIter<T> { IntoIter(self) } } impl<T> Iterator for IntoIter<T> { type Item = T; fn next(&mut self) -> Option<T> { self.0.pop_front() } } impl<T> DoubleEndedIterator for IntoIter<T> { fn next_back(&mut self) -> Option<T> { self.0.pop_back() } } - Why you wouldn't ship this: every operation pays for two atomic-ish ops on the Rc count plus a non-atomic borrow flag flip on the RefCell. Errors that should be compile failures are runtime panics. The peek API leaks
Ref<T>. There is noIterorIterMut. And cycles — which a real circular deque might want — are leaked, because Rc can't collect them.// Costs of every operation, roughly: // - 1+ Rc clone/drop (refcount +/- per neighbour pointer) // - 1+ RefCell borrow (runtime flag check, panics on violation) // - 1 heap allocation for push, 1 free for pop // Compare to the unsafe queue in Chapter 5, which is just a couple of // raw-pointer writes and a Box allocation.