Fuyang Liu
Fuyang Liu

Reputation: 1486

Rust pattern match on a reference of Option one way working but the others way not (borrow checker not pass)

In Rust if a structure is like this

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

Then to implement a method to find the tail and add a new node on it might look like this

fn add1<'a>(mut node: &'a mut Node) {
    while let Some(next) = node.next.as_deref_mut() {
        node = next;
    }
    node.next = Some(Box::new(Node { next: None }));
}

Then error like this encountered:

error[E0506]: cannot assign to `node.next` because it is borrowed
  --> src/lib.rs:16:5
   |
12 | fn add1<'a>(mut node: &'a mut Node) {
   |         -- lifetime `'a` defined here
13 |     while let Some(next) = node.next.as_deref_mut() {
   |                            ------------------------
   |                            |
   |                            borrow of `node.next` occurs here
   |                            argument requires that `node.next` is borrowed for `'a`
...
16 |     node.next = Some(Box::new(Node { next: None }));
   |     ^^^^^^^^^ assignment to borrowed `node.next` occurs here

Some other variation also cannot work:

fn add1_borken_ergonomics(mut node: &mut Node) {
    while let Some(next) = &mut node.next {
        node = next;
    }
    node.next = Some(Box::new(Node { next: None }));
}

fn add1_borken_explicit<'a>(mut node: &'a mut Node) {
    while let Some(ref mut next) = *&mut node.next {
        node = next;
    }
    node.next = Some(Box::new(Node { next: None }));
}

which gives similar error like

error[E0506]: cannot assign to `node.next` because it is borrowed
  --> src/lib.rs:30:5
   |
26 | fn add1_borken_explicit(mut node: &mut Node) {
   |                                   - let's call the lifetime of this reference `'1`
27 |     while let Some(ref mut next) = *&mut node.next {
   |                    ------------     -------------- borrow of `node.next` occurs here
   |                    |
   |                    assignment requires that `node.next` is borrowed for `'1`
...
30 |     node.next = Some(Box::new(Node { next: None }));
   |     ^^^^^^^^^ assignment to borrowed `node.next` occurs here

So the questions is, why writing like this blow it will then work

fn add1_working<'a>(mut node: &'a mut Node) {
    while let Some(ref mut next) = node.next {
        node = next;
    }
    node.next = Some(Box::new(Node { next: None }));
}

The code above should all mean the same but compilers treat them differently?

Rust playground for the example code above. And the same issue can be seen with another simpler code example as well (from the issue page, see below.)

After asked around I found an issue related to this https://github.com/rust-lang/rust/issues/67957, but it seems not mentioning whether it is some compiler bug that can be fixed later, or it is because we need some better understanding in some aspects of the language?

Upvotes: 4

Views: 556

Answers (1)

Hadus
Hadus

Reputation: 1664

This is a known bug.

I got the explanation from the rust forum:

There's another bug report related to this, similar to the one linked in that StackOverflow thread but with a bit more discussion: if/while Some(n) = &mut foo sugar will leak a temporary mutable borrow to current scope in particular situation · Issue #62013 · rust-lang/rust · GitHub

I didn't follow all of the details, but I believe the difference in behavior here is unintentional and will be fixed eventually. When compiling with the next-generation borrow checker Polonius, both functions compile successfully.

Upvotes: 2

Related Questions