Annoying lifetime limitation for exclusive reference

I don't remember seeing people talking about it, but I just encountered this limitation when writing a very simple linked list traversal function, that is, is_some() and unwrap() compiles fine, but while let reports multiple mut borrows error:

struct Node {
    next: Option<Box<Node>>,
}
// this doesn't compile
fn get_tail_mut(head: &mut Node) -> &mut Node {
    let mut tail = head;
    while let Some(next) = tail.next.as_deref_mut() {
        tail = next;
    }
    tail
}
// this compiles
fn get_tail_mut(head: &mut Node) -> &mut Node {
    let mut tail = head;
    while tail.next.is_some() {
        tail = tail.next().as_deref_mut().unwrap();
    }
    tail
}

it's not big deal, I understand why it doesn't compile, and the unwrap() version would generate the optimized code anyway. I also checked different variant of the same code, like using loop + match instead of while let, or match + tail recursion, they all report the same lifetime error

I just wonder, does this limitation has a name that I can lookup for more information, and will it get improved in the near future?

It's called polonius.

Your code runs fine via RUSTFLAGS=-Zpolonius cargo run.

2 Likes

yeah, you have told me about polonius before (in fact, it's my first post on this forum), I'm just too dumb to recognize it's the same pattern. after some tinkering, I came to following code, which I then recognized.

pub fn get_tail_node_recur(head: &mut Node<i32>) -> &mut Node<i32> {
    match head.next.as_deref_mut() {
        Some(next) => return get_tail_node_recur(next),
        None => {},
    }
    head
}
1 Like

Polonius is the likely fix, but the limitation is NLL Problem Case #3.[1]


  1. There's a few dupe/variant issues too IIRC. ↩ī¸Ž

3 Likes

thanks for the link.