Learn Rust With Entirely Too Many Linked Lists
25

Symmetric Cases

Everything we did for the front exists for the back, with head and tail swapped and next and prev swapped. The _mut peek variants are the same shape but call borrow_mut and return RefMut<T> instead of Ref<T>. Boilerplate, no new ideas.

  1. push_back, pop_back, peek_back: text-substitute the front versions. head becomes tail, tail becomes head, next becomes prev, prev becomes next. Same code, mirrored.
    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
        })
    }
  2. The _mut peek variants exist because mutable access through a RefCell is a different guard type. borrow_mut() returns RefMut<'_, T>, which is exclusive. RefMut::map projects it the same way Ref::map does for shared.
    use std::cell::RefMut;
    
    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))
    }