It seems that u, a mutable borrow, becomes automatically immutable in
let v = &*u;
Both u and v are then immutable borrowed references so they are both allowed.
use std::ascii::AsciiExt;
fn show(a: &str) {
println!("a={}", a);
}
fn main() {
let mut t = String::new();
t.push('s');
let u = &mut t;
u.make_ascii_uppercase(); // u is really mutable here
let v = &*u; // u became immutable to allow this?
show(u); // both u and v are now accessible!
show(v);
}
Outputs:
a=S
a=S
If I try to use u as a mutable borrow after
show(v);
compiler will recall that
let v = &*u;
is really not allowed:
cannot borrow `*u` as mutable because it is also borrowed as immutable
Is it a bug or is there really some "automatically convert mutable borrow to immutable when mutability is no longer needed" principle? I am using Rust 1.13.0.
A mutable reference can be borrowed immutably, however this is not what is happening here.
When forming a reference with &, you need to be explicit about mutability; unless you specify &mut it will be an immutable reference.
Your example can be reduced to:
use std::ascii::AsciiExt;
fn main() {
let mut t = "s".to_string();
let u = &mut t;
u.make_ascii_uppercase();
let v = &*u;
let () = v;
}
The last line is a trick to get the compiler to tell us (in the error message) what the type of v is. It reports:
error[E0308]: mismatched types
--> <anon>:9:9
|
9 | let () = v;
| ^^ expected reference, found ()
|
= note: expected type `&std::string::String`
= note: found type `()`
Here we have:
u: an immutable binding, which is a mutable borrow of tv: an immutable binding, which is an immutable re-borrow of t through uIf, however, I change the v line to let v = &mut *u;, then I get expected type '&mut std::string::String' and then we have:
u: an immutable binding, which is a mutable borrow of tv: an immutable binding, which is a mutable re-borrow of t through uThe important concept here is re-borrowing, which is what &*u and &mut *u are about. Re-borrowing allows forming a new reference from an existing reference:
The re-borrowing rules are relatively simple, they mirror the borrowing rules:
It is interesting to note that a re-borrowed reference can live longer than the reference it was formed from:
fn main() {
let mut t = "s".to_string();
let v;
{
let u = &mut t;
v = &mut *u;
}
v.make_ascii_uppercase();
show(v);
}
This is necessary to ensure that you can return a reference from functions; of course.
So, ultimately, a re-borrow is tracked down to the original borrowed value by the compiler; however, due the re-borrowing mechanics it allows forming an immutable reference to this original value even though a mutable reference is in scope... and simply make sure that this mutable reference is unusable for the lifetime of the new immutable reference.
When a function takes a reference, the compiler automatically introduces a re-borrow at the call site with the appropriate mutability; this is what happens with show here: show(u) really is show(&*u) with a new immutable reference formed for the duration of the function call.
This is confusing, so let's do some experiments.
Your code compiles:
let mut t = String::new();
t.push('s');
let u = &mut t;
u.make_ascii_uppercase(); // u is really mutable here
let v = &*u; // u became immutable to allow this?
show(u); // both u and v are now accessible!
show(v);
What happens if we change the let v line to:
let v = &t;
error[E0502]: cannot borrow
tas immutable because it is also borrowed as mutable--> :12:14
Ok, so that's different. That tells me that &*u, despite being the same type as &t, is not the same; the former is (sub-)borrowing from u, but the latter is trying to reborrow t.
Let's try a different experiment. Putting the previous line back, but now adding something new:
let v = &*u; // the original reborrow
let w = u; // Try to move out of `u`
error[E0502]: cannot borrow
tas immutable because it is also borrowed as mutable--> :12:14
Aha! That confirms that v really is borrowing from u rather than directly from t.
Now, in the original, let's add an attempted mutation via u to the end:
let mut t = String::new();
t.push('s');
let u = &mut t;
u.make_ascii_uppercase(); // u is really mutable here
let v = &*u; // u became immutable to allow this?
show(u); // both u and v are now accessible!
show(v);
u.make_ascii_uppercase();
Now I get:
error[E0502]: cannot borrow
*uas mutable because it is also borrowed as immutable
I think that basically explains what's going on:
u borrows t mutably. This stops t being accessed at all directly.v borrows u immutably. This means that u can still be used immutably, but it can't be used mutably or moved out of.u can't be borrowed mutably while v exists, you can't use *u mutably either. (This last bit is slightly handwavey; I'd welcome further clarifications...)If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With