Object still borrowed after break out of loop scope?

Hi, I wrote a simple linked list, which has a pop function (pop last Node, and return it's value). I search for the last Node that has a next field of value None, and assign last Node=None.

But the compiler complain that after I break out of loop, borrow still exist, so that I cannot assign value. Any suggestion? Thanks!

pub struct SimpleLinkedList<T> {
    head: Option<Box<Node<T>>>,
}

struct Node<T> {
    data: T,
    next: Option<Box<Node<T>>>,
}

impl<T: PartialEq + Copy> SimpleLinkedList<T> {
    pub fn pop(&mut self) -> Option<T> {
        let mut ptr = &mut self.head;
        let mut ret = None;
        loop {
            match ptr {
                 //Borrow happened
                Some(ref mut node) => {
                    if node.next == None {
                        ret = Some(node.data);
                        break;
                    } else {
                        ptr = &mut node.next;
                    }
                }
                None => {
                    return None;
                }
            }
        }
        // Out of loop scope, but still borrowed?
        *ptr = None;
        ret
    }
}
error[E0506]: cannot assign to `*ptr` because it is borrowed
  --> src\lib.rs:74:9
   |
61 |                 Some(ref mut node) => {
   |                      ------------ borrow of `*ptr` occurs here
...
74 |         *ptr = None;
   |         ^^^^
   |         |
   |         assignment to borrowed `*ptr` occurs here
   |         borrow later used here

This is a known kind of limitation of the borrow checker, you code is not really wrong.

There’s also a new borrow checker WIP, called polonius, that won’t have this limitation, currently available with an experimental flag on nightly with which your code compiles as is. It’s sometimes useful to convince yourself that something seemingly straightforward that doesn’t work really is the compiler’s fault.

The general problem is that the current borrow checker isn’t always able to do a fine-grained lifetime analysis of references that are used differently in different branches of an if/match/etc.

For now, re-creating the mutable reference (using unwrap or similar) once you’re committed on the “problematic“ branch can sometimes help, as it does in this case, i.e.

ptr = &mut ptr.as_mut().unwrap().next;

(playground)

I also fixed the compiler error I got from the “== None”. Perhaps your less minimized version of Node supports Eq, but .is_none() is a useful and clear method call in either case.

3 Likes

Thank you!!! :star_struck: