After reading about Rust's scopes and references, I wrote a simple code to test them.
fn main() {
// 1. define a string
let mut a = String::from("great");
// 2. get a mutable reference
let b = &mut a;
b.push_str(" breeze");
println!("b = {:?}", b);
// 3. A scope: c is useless after exiting this scope
{
let c = &a;
println!("c = {:?}", c);
}
// 4. Use the mutable reference as the immutable reference's scope
// is no longer valid.
println!("b = {:?}", b); // <- why does this line cause an error?
}
As far as I understand:
- Immutables and mutables cannot be used simultaneously.
- 2 mutables of same object cannot exist.
- But scoping can allow 2 mutable to exist as long as 2 mutables are not in
the same scope.
Expectation
In 3
, c
is created within a scope, and no mutables are used in it. Hence,
when c
goes out of scope, it is clear that c
will not be used anymore (as
it would be invalid) and hence b
, a mutable reference, can be used safely in
4
.
The expected output:
b = "great breeze"
c = "great breeze"
b = "great breeze"
Reality
Rust produces the following error:
error[E0502]: cannot borrow `a` as immutable because it is also borrowed as mutable
--> src/main.rs:12:17
|
6 | let b = &mut a;
| ------ mutable borrow occurs here
...
12 | let c = &a;
| ^^ immutable borrow occurs here
...
18 | println!("b = {:?}", b); // <- why does this line cause an error?
| - mutable borrow later used here
Inference
(This is just what I think is happening, and can be a fallacy)
It seems that no matter what, you cannot use any mutable references (b
)
once an immutable reference (c
) was made (or "seen" by the rust
compiler), whether in a scope or not.
This is much like:
At any given time, you can have either one mutable reference or any
number of immutable references.
This situation is similar to:
let mut a = String::from("great");
let b = &mut a;
b.push_str(" breeze");
println!("b = {:?}", b);
// ^ b's "session" ends here. Using 'b' below will violate the rule:
// "one mutable at a time"
// can use multiple immutables: follows the rule
// "Can have multiple immutables at one time"
let c = &a;
let d = &a;
println!("c = {:?}, d = {:?}", c, d);
println!("b = {:?}", b); // !!! Error
Also, as long as we are using immutable references, the original object
or reference becomes "immutable". As said in Rust Book:
We also cannot have a mutable reference while we have an immutable one.
Users of an immutable reference don’t expect the values to suddenly
change out from under them!
and
...you can have either one mutable reference...
let mut a = String::from("great");
let b = &mut a;
b.push_str(" breeze");
println!("b = {:?}", b);
let c = &b; // b cannot be changed as long as c is in use
b.push_str(" summer"); // <- ERROR: as b has already been borrowed.
println!("c = {:?}", c); // <- immutable borrow is used here
So this code above somewhat explains @Shepmaster's solution.
Going back to the original code and removing the scope:
// 1. define a string
let mut a = String::from("great");
// 2. get a mutable reference
let b = &mut a;
b.push_str(" breeze");
println!("b = {:?}", b);
// 3. No scopes here.
let c = &a;
println!("c = {:?}", c);
// 4. Use the mutable reference as the immutable reference's scope
// is no longer valid.
println!("b = {:?}", b); // <- why does this line cause an error?
Now it is clear why this code has an error. The rust compiler sees that
we are using a mutable b
(which is a mutable reference of a
,
therefore a
becomes immutable) while also borrowing an immutable
reference c
. I like to call it "no immutables in between".
Or we can also call it "un-sandwiching". You cannot have/use a mutable
between "immutable declaration" and "immutable use" and vice-versa.
But this still does not answer the question of why scopes fail here.
Question
- Even after explicitly moving
c
into a scope, why does the Rust
compiler produce this error message?
See Question&Answers more detail:
os