Search code examples
data-structuresrustborrow-checkercons

How to pop a value from cons list?


In the chapter 15.1 of The Book an example of Box<> usage for recursive type (cons list) implementation is shown. I tried to implement a method for this cons list to pop the outermost value out of the list, leaving the list with whatever left or Nil if nothing left. But it doesn't work, I can't figure out how to return the value while mutating the self after its deconstruction (and so borrowing?). Really none of the references in method make sense to me....

Is there no way to do this without creating a function that consumes the list and spits out both the value and new list?

Here is my code:

use crate::List::{Cons, Nil};

fn main() {
    let mut list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));

    println!("The first value is: {}.", list.pop().unwrap());
    println!("The second value is: {}.", list.pop().unwrap());
}

#[derive(Debug)]
enum List {
    Cons(i32, Box<List>),
    Nil,
}

impl List {
    // It seems to me I need to mutably borrow the list to change it
    // but the way reference types behave later confuses me
    fn pop(&mut self) -> Option<i32> {
        if let Cons(value, list) = &self {
            self = **list; // <- how to do this bit? self is borrowed...
            Some(*value)
        } else {
            None
        }
    }
}

Solution

  • You can make your approach work by first moving the current list out of self, and replacing it with Nil. This way, you can match on the old list and still be able to assign to self:

    fn pop(&mut self) -> Option<i32> {
        let old_list = std::mem::replace(self, Nil);
        match old_list {
            Cons(value, tail) => {
                *self = *tail;
                Some(value)
            }
            Nil => None,
        }
    }
    

    (Playground)