Consider code:
struct Y<'a>(&'a i32);
fn foo<'a, 'b>(x: &'a mut i32, y: &'b mut Y<'a>) {
bar(x, y);
// error[E0499]: cannot borrow `*x` as mutable more than once at a time
bar(x, y);
}
fn bar<'a, 'b>(x: &'a mut i32, y: &'b mut Y<'a>) {}
Second call to bar(x, y)
gives error:
error[E0499]: cannot borrow `*x` as mutable more than once at a time
--> src/main.rs:7:9
|
5 | fn foo<'a, 'b>(x: &'a mut i32, y: &'b mut Y<'a>) {
| -- lifetime `'a` defined here
6 | bar(x, y);
| ---------
| | |
| | first mutable borrow occurs here
| argument requires that `*x` is borrowed for `'a`
7 | bar(x, y);
| ^ second mutable borrow occurs here
I don’t understand this error because it doesn’t say a word about y
, however problem is somehow related to y
because if I remove y
there will be no error. But why x
and y
have some sort of conflict here? They are completely unrelated variables, they are not borrowing each other, etc. The only thing is that they have the same lifetime, but why it is a problem?
4
Your function signature dictates that the referece x
is borrowed for however long Y
hold its reference. So when you call bar
which has that same signature, you are passing x
for all of 'a
. A single call would be okay, but another call means you are again trying to pass x
for all of 'a
, but the previous call already borrowed it for 'a
. And since mutable references are exclusive, you get a conflict.
If you were to relax y
to an immutable reference, &'b Y<'a>
, then this would work since the compiler would be allowed to pick a shorter 'a
for the calls to bar
. However because mutable references’ referants are invariant, the compiler cannot do that. See Subtyping and Variance.
This is preventing you from a potential error: with the signature, bar
could easily do this:
fn bar<'a, 'b>(x: &'a mut i32, y: &'b mut Y<'a>) {
y.0 = x;
}
You have constrained that the lifetimes are the same and mutable references can be downgraded to immutable references, so this compiles. Though I hope you see how this is a problem for foo
attempting to call this function twice – you would get a simultaneous mutable reference (x
) and an immutable reference y.0
to the same value. Rust does not allow this.
I’m no an expert on lifetime, but I believe this is because on the first bar()
call, by defining lifetime of bar
as
fn bar<'a>(x: &'a mut i32, y: &mut Y<'a>) {}
you are saying the mut borrow of x by bar()
lives as long as y (the value it self, not the reference).
So when you call bar()
the second time, because y still exists, the first mut borrow of x is also not dropped, so x cannot be mut borrowed a second time.
Notice you did not define how long the mut reference of y actually lives. If you do so:
fn foo<'a>(x: &'a mut i32, y: &'a mut Y<'a>) {
bar(x, y);
bar(x, y);
}
fn bar<'a>(x: &'a mut i32, y: &'a mut Y<'a>) {}
Both x and y will be shown as error having been borrowed more than once
2