Borrowing Error when 2 variables exists inside/outside a scope - rust

I need advice about borrowing in this code, specifically the last 4 lines.
I realize after the closing curly brace, variable e cannot be printed outside the scope and will cause an error if variable c (inside scope) is NOT commented out. Similarly, variable c cannot be printed inside scope and will cause an error if variable e (outside scope) is NOT commented out
In summary,
Line 1 removed, Line 2 stays: No Error
Line 1 stays, Line 2 removed: No Error
Line 1 & 2 stays: Error
fn main() {
let mut c = String::from("hello");
println!("c1 {}", c);
c = String::from("hello2");
println!("c2 {}", c);
let e = &mut c;
println!("e1 {}", e);
e.pop();
println!("e2 {}", e);
// println!("c {}", c); //*! <- Error Immutable Borrow
{
e.pop();
e.pop();
println!("e3 {}", e);
println!("c3 {}", c); //*? Line 1: Error Immutable Borrow when e4 is NOT commented out
}
// println!("e4 {}", e); //*? Line 2: Error Immutable Borrow when c3 is NOT commented out
println!("c4 {}", c);
}
The error:
error[E0502]: cannot borrow `c` as immutable because it is also borrowed as mutable
--> src/main.rs:14:22
|
8 | let e = &mut c;
| ------ mutable borrow occurs here
...
14 | println!("c {}", c);
| ^ immutable borrow occurs here
...
17 | e.pop();
| - mutable borrow later used here
What is the reason for this behavior?

As a general rule a borrow will last until it goes out of the scope it was defined. In programming a what we normally refer to as a "scope" is formally known as a Lexical Scope.
Before rust got Non Lexical Lifetimes explained thoroughly in the link provided by Jmb, your example wouldn't compile in either case. This actually makes borrowing was a bit easier to learn since rules are more consistent but a lot more annoying in practice.
You can try this yourself by using the Godbolt Compiler Explorer setting the rustc version < 1.31. An example of this can be found here. As you see that gives a compiler error on version 1.30.
Example 1
let mut a = String::from("hello");
let b = &mut a;
println!("b1 {}", b);
println!("a1 {}", a);
One of the ways to solve that earlier was to add a new lexical scope like this:
Example2
let mut a = String::from("hello");
{
let b = &mut a;
println!("b1 {}", b);
}
println!("a1 {}", a);
In this case the exclusive &mut a borrow would only last until the end of the lexical scope it was in.
Now, it's obvious from looking at example 1 that we don't actually use the &mut a borrow after println!("b1 {}", b);, so intuitively for us as programmers having to insert a scope only to explain this to the compiler seems overly strict.
In addition, if we declare any other variable after let b = &mut a; we want to use after it's scope that won't work so we need to adapt and alter the way we write our programs only to please the borrow checker.
Rust later got what's called Non Lexical Lifetimes, which is just what the label says. Lifetimes that is not tied strictly to the Lexical Scope it's in.
Now, the compiler checks when you stop using a borrow and inserts an invisible "lifetime scope" (or at least you can think of it like that) just as in example 2, but without the downsides of introducing a true lexical scope.
That's why you're experiencing a bit of "magic" that might come off as a bit surprising in your example. I've tried to explain by showing the scope the compiler will use to check if a shared & borrowed of the pointer to c is allowed or not.
However, trust me, this little addition to the learning curve about the borrow checker is absolutely worth it once you realize some of the hoops you need to jump through without it.
let mut c = String::from("hello");
println!("c1 {}", &c);
c = String::from("hello2");
println!("c2 {}", &c);
// "lifetime scope" of `&mut c`
let e = &mut c; //{ |----------------------
println!("e1 {}", &e); // | |
// | |
e.pop(); // | |
// | |
println!("e2 {}", &e); // | |
// println!("c {}", &c); // | | (e3)
// | |
{ // | (e4) |
e.pop(); // | |
e.pop(); // | |
// | |
println!("e3 {}", &e); // }<-----|-------
println!("c3 {}", &c); // |
} // |
// |
// println!("e4 {}", &e);// }<------
println!("c4 {}", &c);
I've explicitly shown that println! takes a borrow, and doesn't take ownership to make the code a bit clearer when talking about lifetimes and borrows.
Normally passing in c instead of &c to a function passes on the ownership, but println! is a macro (not a function) which, in this case, only re-borrows it. The distinction can be important when learning about borrows and lifetimes.

Related

borrow disjoint fields of a struct

struct C {
p: String,
q: String,
}
impl C {
fn call(&mut self) {}
}
fn main(){
let mut c = C { p: "p".to_string(), q: "q".to_string() };
let r_c = &mut c;
let p = &mut r_c.p;
let q = &mut r_c.q; // ???
r_c.call();
p.push('x');
q.push('y');
}
Fails with (playground)
error[E0499]: cannot borrow `*r_c` as mutable more than once at a time
--> src/main.rs:15:9
|
13 | let p = &mut r_c.p;
| ---------- first mutable borrow occurs here
14 | let q = &mut r_c.q; // ???
15 | r_c.call();
| ^^^^^^^^^^ second mutable borrow occurs here
16 | p.push('x');
| ----------- first borrow later used here
I know how to fix error[E0499]. I just don't understand that
mutable borrow occurs in let p = &mut r_c.p;, but why does mutable borrow not occur in let q = &mut r_c.q;?
let p = &mut r_c.p; // --------------------------+borrow `*r_c` first
let q = &mut r_c.q; // ----+borrow `*r_c` second |
// | |
p.push('x'); // ----|---------------------+
q.push('y'); // ----+
if mutable borrow occurs in let q = &mut r_c.q;, the code above should be error. But in fact, the code above compiles.
The first borrow p is enough to be in conflict with the usage of r_c.
No need to repeat the same diagnostic with q.
If you swap these two lines, you have the same message about q.
If you reorder this way
let r_c = &mut c;
r_c.call();
let p = &mut r_c.p;
let q = &mut r_c.q; // ???
p.push('x');
q.push('y');
these conflicts disappear since r_c is not used anymore after .call().
These exclusive references don't really hurt as long as you don't use them; i.e. pass them to functions.
I guess you introduced these references for an experimentation purpose (good thing), but in practice we should try to avoid letting them span over long portions of code (between their creation and their last usage) because conflicts with other operations may occur in the middle.
From the edited question:
but why does mutable borrow not occur in let q = &mut r_c.q;?
It does occur.
The error is on r_c.call() and the message shows the first reference (p) causing this error; the message is first mutable borrow occurs here.
Even if the second reference (q) did not exist, the error would be the same.
If twelve references were taken here, the error would still be the same; no need to repeat the same help message many times.

Why is rust allowing 2 simultaneous mutable references? [duplicate]

This question already has an answer here:
Why does the Rust compiler error with "cannot borrow as immutable because it is also borrowed as mutable" after moving the variable into a scope?
(1 answer)
Closed 1 year ago.
The Rust Book says
"you can have only one mutable reference to a particular piece of data in a particular scope"
As far as I can tell, the following working code contradicts this by having 2 simultaneous mutable references:
fn main() {
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s;
}
The code is just a slight modification to this example from the book:
fn main() {
let mut s = String::from("hello");
{
let r1 = &mut s;
} // r1 goes out of scope here, so we can make a new reference with no problems.
let r2 = &mut s;
}
The book implies that it's only possible to make the 2nd mutable reference because the first one went out of scope, but when I modified the code so that the first reference does not go out of scope, it still compiles. Why is this allowed? I thought Rust was only supposed to allow 1 mutable reference at a time.
The r1 is still in scope, but you won't be able to use it. That's because it borrowed s. But once r2 borrowed it, r1 is no more use-able.
For example, try adding
dbg!(r1);
You should get an error:
|
15 | let r1 = &mut s;
| ------ first mutable borrow occurs here
16 | let r2 = &mut s;
| ^^^^^^ second mutable borrow occurs here
17 |
18 | dbg!(r1);
| -- first borrow later used here

Rust language : immutable borrow and mutable borrow error (2 cases) [duplicate]

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?
Your question is why doesn't compiler allow c to refer to data which is already mutably borrowed. I for one would expect that to be disallowed to begin with!
But - when you comment out the very last println!(), the code compiles correctly. Presumably that's what led you to conclude that aliasing is allowed, "as long as mutables aren't in the same scope". I argue that that conclusion is incorrect, and here is why.
While it's true that there are some cases where aliasing is allowed for references in sub-scopes, it requires further restrictions, such as narrowing an existing reference through struct projection. (E.g. given a let r = &mut point, you can write let rx = &mut r.x, i.e. temporarily mutably borrow a subset of mutably borrowed data.) But that's not the case here. Here c is a completely new shared reference to data already mutably referenced by b. That should never be allowed, and yet it compiles.
The answer lies with the compiler's analysis of non-lexical lifetimes (NLL). When you comment out the last println!(), the compiler notices that:
b isn't Drop, so no one can observe a difference if we pretend it was dropped sooner, perhaps immediately after last use.
b is no longer used after the first println!().
So NLL inserts an invisible drop(b) after the first println!(), thereby allowing introduction of c in the first place. It's only because of the implicit drop(b) that c doesn't create a mutable alias. In other words, the scope of b is artificially shortened from what would be determined by purely lexical analysis (its position relative to { and }), hence non-lexical lifetime.
You can test this hypothesis by wrapping the reference in a newtype. For example, this is equivalent to your code, and it still compiles with the last println!() commented out:
#[derive(Debug)]
struct Ref<'a>(&'a mut String);
fn main() {
let mut a = String::from("great");
let b = Ref(&mut a);
b.0.push_str(" breeze");
println!("b = {:?}", b);
{
let c = &a;
println!("c = {:?}", c);
}
//println!("b = {:?}", b);
}
But, if we merely implement Drop for Ref, the code no longer compiles:
// causes compilation error for code above
impl Drop for Ref<'_> {
fn drop(&mut self) {
}
}
To explicitly answer your question:
Even after explicitly moving c into a scope, why does the Rust compiler produce this error message?
Because c is not allowed to exist alongside b to begin with, regardless of being an an inner scope. When it is allowed to exist is in cases where the compiler can prove that b is never used in parallel with c and it's safe to drop it before c is even constructed. In that case aliasing is "allowed" because there's no actual aliasing despite b "being in scope" - on the level of the generated MIR/HIR, it's only c that refers to the data.

Why does Rust compiler not detecting an unused-borrowed reference?

Why does the following fail to compile (2018 edition)
fn main() {
let mut s = String::from("hello");
let r1 = &mut s;
{
let _r2 = &s;
}
println!("{}", r1);
}
Why the compiler fails to notice that _r2 is unused and successfully compile the above?
It complains that we have both mutable and immutable refs and mutable is used in println.
the confusing part is that literature (books etc...) talk about "using block to restrict borrowing scope. "Beginning Rust" p348 for example. So I found it confusing that we advocate for such practices but it (sometimes) does not work.
But it does work here; the scope of _r2 is restricted by the block it's
declared in. The problem is just that r1 is visible in this scope as well.
That is, the error message is not saying "you can't println because there are both mutable and immutable borrows at that point", but "you can't declare _r2 because there is already a mutable borrow at that point". println only appears as a reason why r1 is still alive at the point of _r2's declaration.
You can see it from the error message:
error[E0502]: cannot borrow `s` as immutable because it is also borrowed as mutable
--> src/main.rs:5:19
|
3 | let r1 = &mut s;
| ------ mutable borrow occurs here
4 | {
5 | let _r2 = &s;
| ^^ immutable borrow occurs here
6 | }
7 | println!("{}", r1);
| -- mutable borrow later used here
^^ points at the actual error location; -- are other related locations.
You can only do like this,
fn main(){
let mut s = String::from("hello");
let r1 = &mut s;
{
let r2 = &r1;
}
println!("{}", r1);
}

Why is Rust forcing to use move in case of i32 while spawing threads?

I'm new to Rust and looks like I'm seriously missing some concept here.
use std::thread;
fn main() {
let mut children = vec![];
//spawn threads
for i in 0..10 {
let c = thread::spawn(|| {
println!("thread id is {}", i);
});
children.push(c);
}
for j in children {
j.join().expect("thread joining issue");
}
}
It fails with the error:
error[E0373]: closure may outlive the current function, but it borrows `i`, which is owned by the current function
Since the type of i is i32 , and there are no references involved shouldn't Rust copy the value instead of forcing to move ?
The answer to your original question is that println! borrows its arguments. However, as you pointed out in the comments, even (apparently) moving the integer into the closure still causes a compile error.
For the purposes of this answer, we'll work with this code.
fn use_closure<F: FnOnce() + 'static>(_: F) {}
fn main() {
let x: i32 = 0;
use_closure(|| {
let _y = x;
});
}
(playground)
use_closure simulates what thread::spawn does in the original code: it consumes a closure whose type has to be 'static.
Attempting to compile this gives the error
error[E0373]: closure may outlive the current function, but it borrows `x`, which is owned by the current function
--> src/main.rs:5:17
|
5 | use_closure(|| {
| ^^ may outlive borrowed value `x`
6 | let _y = x;
| - `x` is borrowed here
|
note: function requires argument type to outlive `'static`
--> src/main.rs:5:5
|
5 | / use_closure(|| {
6 | | let _y = x;
7 | | });
| |______^
help: to force the closure to take ownership of `x` (and any other referenced variables), use the `move` keyword
|
5 | use_closure(move || {
| ^^^^^^^
Wait, what?
6 | let _y = x;
| - `x` is borrowed here
Why is x borrowed there? Shouldn't it be a copy? The answer lies in "capture modes" for closures. From the documentation
The compiler prefers to capture a closed-over variable by immutable borrow, followed by unique immutable borrow (see below), by mutable borrow, and finally by move. It will pick the first choice of these that allows the closure to compile. The choice is made only with regards to the contents of the closure expression; the compiler does not take into account surrounding code, such as the lifetimes of involved variables.
Precisely because x has a Copy type, the closure itself can compile with a mere immutable borrow. Given an immutable borrow of x (call it bor), we can do our assignment to _y with _y = *bor. This isn't a "move out of data behind a reference" because this is a copy instead of a move.
However, since the closure borrows a local variable, its type won't be 'static, so it won't be usable in use_closure or thread::spawn.
Trying the same code with a type that isn't Copy, it actually works perfectly, since the closure is forced to capture x by moving it.
fn use_closure<F: FnOnce() + 'static>(_: F) {}
fn main() {
let x: Vec<i32> = vec![];
use_closure(|| {
let _y = x;
});
}
(playground)
Of course, as you already know, the solution is to use the move keyword in front of the closure. This forces all captured variables to be moved into the closure. Since the variable won't be borrowed, the closure will have a static type and will be able to be used in use_closure or thread::spawn.
fn use_closure<F: FnOnce() + 'static>(_: F) {}
fn main() {
let x: i32 = 0;
use_closure(move || {
let _y = x;
});
}
(playground)

Resources