Background
Consider a toy problem in which I have a Node
struct that represents nodes of linked lists and that I want to create a function that builds a list with values from 1 to 9. The following code works as expected:
struct Node {
val: i32,
next: Option<Box<Node>>,
}
fn build_list() -> Option<Box<Node>> {
let mut head = None;
let mut tail = &mut head;
for n in 1..10 {
*tail = Some(Box::new(Node {val: n, next: None}));
if let Some(ref mut x) = tail {
tail = &mut x.next;
};
}
head
}
But if I modify the match expression in the build_list
function to the following, it fails to compile:
fn build_list() -> Option<Box<Node>> {
let mut head = None;
let mut tail = &mut head;
for n in 1..10 {
*tail = Some(Box::new(Node {val: n, next: None}));
if let Some(x) = tail.as_mut() {
tail = &mut x.next;
};
}
head
}
The compilation error:
error[E0506]: cannot assign to `*tail` because it is borrowed
--> src/main.rs:72:9
|
72 | *tail = Some(Box::new(Node {val: n, next: None}));
| ^^^^^
| |
| assignment to borrowed `*tail` occurs here
| borrow later used here
73 | {
74 | if let Some(x) = tail.as_mut() {
| ---- borrow of `*tail` occurs here
error[E0499]: cannot borrow `*tail` as mutable more than once at a time
--> src/main.rs:74:30
|
74 | if let Some(x) = tail.as_mut() {
| ^^^^ mutable borrow starts here in previous iteration of loop
Question
In this example, what is the difference between
if let Some(ref mut x) = tail
and
if let Some(x) = tail.as_mut()
?
(As a beginner learning Rust) I was expecting those match expressions to be equivalent, but apparently there's some subtle difference that I'm missing.
Update
I cleaned up the code from my original example so that I don't need a placeholder element for the head of the list. The difference (and compilation error) still remains, I just get an additional compilation error for assigning to borrowed *tail
.
Update 2
(This is just a curious observation, doesn't help answering the original question)
After considering @Emoun's answer, it sounded important (in the first working example) that the compiler should be able to know that tail
was changing at every iteration of the loop (such that it could make sure &mut x.next
being borrowed each time was different). So I made an experiment to change the code in a way that the compiler wouldn't be able to tell if that was the case by adding a if n % 2 == 0
condition to the tail = &mut x.next;
assignment. Sure enough, it resulted in a compilation error similar to the other one:
fn build_list() -> Option<Box<Node>> {
let mut head = None;
let mut tail = &mut head;
for n in 1..10 {
*tail = Some(Box::new(Node {val: n, next: None}));
if let Some(ref mut x) = tail {
if n % 2 == 0 {
tail = &mut x.next;
}
};
}
head
}
The new error:
error[E0506]: cannot assign to `*tail` because it is borrowed
--> src/main.rs:60:9
|
60 | *tail = Some(Box::new(Node {val: n, next: None}));
| ^^^^^
| |
| assignment to borrowed `*tail` occurs here
| borrow later used here
61 | if let Some(ref mut x) = tail {
| --------- borrow of `*tail` occurs here
error[E0503]: cannot use `*tail` because it was mutably borrowed
--> src/main.rs:61:16
|
61 | if let Some(ref mut x) = tail {
| ^^^^^---------^
| | |
| | borrow of `tail.0` occurs here
| use of borrowed `tail.0`
| borrow later used here
error[E0499]: cannot borrow `tail.0` as mutable more than once at a time
--> src/main.rs:61:21
|
61 | if let Some(ref mut x) = tail {
| ^^^^^^^^^ mutable borrow starts here in previous iteration of loop