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);
}
Related
Let's say I have this code:
let mut s = "hi".to_string();
let c = || s.push_str(" yo");
c();
It doesn't compile and generates this error:
error[E0596]: cannot borrow `c` as mutable, as it is not declared as mutable
--> src\test.rs:120:9
|
119 | let c = || s.push_str(" yo");
| - - calling `c` requires mutable binding due to mutable borrow of `s`
| |
| help: consider changing this to be mutable: `mut c`
120 | c();
| ^ cannot borrow as mutable
For more information about this error, try `rustc --explain E0596`.
error: could not compile `test` due to previous error
Why c(); cannot borrow as mutable? It cannot borrow what as mutable? c? In order to make it work, I have to change it to:
let mut s = "hi".to_string();
let mut c = || s.push_str(" yo");
c();
But here I'm just defining a closure c. I'll not modify it, like c = || x;. Why must define it as let mut c?
But here I'm just defining a closure c. I'll not modify it, like c = || x;. Why must define it as let mut c?
Because a closure is fundamentally a structure, each captured item being a member of that structure.
So your code is roughly equivalent to this:
struct S<'a>(&'a mut String);
impl S<'_> {
fn call(&mut self) {
self.0.push_str("yo");
}
}
fn main() {
let mut s = "hi".to_string();
let c = S(&mut s);
c.call();
}
And that fails to compile with more or less the same error:
error[E0596]: cannot borrow `c` as mutable, as it is not declared as mutable
--> src/main.rs:14:5
|
13 | let c = S(&mut s);
| - help: consider changing this to be mutable: `mut c`
14 | c.call();
| ^^^^^^^^ cannot borrow as mutable
Now you might object that that's because I defined fn call(&mut self), but if you don't you get the internal part of the same error:
error[E0596]: cannot borrow `*self.0` as mutable, as it is behind a `&` reference
--> src/main.rs:8:9
|
7 | fn call(&self) {
| ----- help: consider changing this to be a mutable reference: `&mut self`
8 | self.0.push_str("yo");
| ^^^^^^^^^^^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable
That is you can't modify an &mut through an &. Because if you did you could create multiple &s to the same &mut through which you could modify the pointee, and that would essentially give you multiple &mut.
Which is not allowed by Rust's semantics:
At any given time, you can have either one mutable reference or any number of immutable references.
It may not look like c needs to be mutable because you aren't modifying c itself, but for closure types, the mut keyword is what allows it to mutate any external state. It changes the type of the closure to be a FnMut, which the docs refer to as "a mutably capturing closure".
It's about ownership. mut here does not mean that you will mutate c, but that you require mut-level access to the closure in order to execute it.
c borrows s mutably. This should be fairly obvious, because calling c() modifies s.
Now imagine if c would be usable without mut:
let mut s = "hi".to_string();
let c = || s.push_str(" yo");
let c1 = &c;
let c2 = &c;
c1();
c2();
Here, both c1 and c2 would have mutable access to s simultaneously, which is not possible according to Rusts borrowing rules. This is prevented automatically by the compiler, as it changes the type of the closure to FnMut as soon as you access a variable mutably from within the closure.
Then, you need mut access to the closure to execute it and the case becomes a compiler error:
let mut s = "hi".to_string();
let mut c = || s.push_str(" yo");
let c1 = &mut c;
let c2 = &mut c;
c1();
c2();
error[E0499]: cannot borrow `c` as mutable more than once at a time
--> src/main.rs:5:10
|
4 | let c1 = &mut c;
| ------ first mutable borrow occurs here
5 | let c2 = &mut c;
| ^^^^^^ second mutable borrow occurs here
6 |
7 | c1();
| -- first borrow later used here
As per my understanding, there can be multiple immutable references at a time but if there is a mutable reference it can be the only usable reference.
Why does the following code work?:
fn main() {
let mut y = String::from("bar");
let f: &mut String = &mut y;
let f2: &String = &(*f);
// not allowed since mutable reference already exists
// let f3: &String = &y;
println!("{}, ", f.as_str());
println!("{}", f2.as_str());
}
Edit: Another part of my question which I guess isn't obvious is: why am I not allowed to create f3 (like I am doing in the commented line) when it is exactly the same thing as f2 and created similarly by refrencing y.
Because the compiler is smart enough to know if the data actually is used as mutable before it is used as immutable. If you change the code at all to use the mutable reference first, it fails.
fn main() {
let mut y = String::from("bar");
let f: &mut String = &mut y;
let f2: &String = &(*f);
f.clear();
// not allowed since mutable reference already exists
// let f3: &String = &y;
println!("{}, ", f.as_str());
println!("{}", f2.as_str());
}
Here's a link to a live example. The error, as you'd expect, mentions you cannot have an immutable reference if a mutable one exists.
error[E0502]: cannot borrow `*f` as mutable because it is also borrowed as immutable
--> src/main.rs:6:5
|
5 | let f2: &String = &(*f);
| ----- immutable borrow occurs here
6 | f.clear();
| ^^^^^^^^^ mutable borrow occurs here
...
12 | println!("{}", f2.as_str());
| ----------- immutable borrow later used here
For more information about this error, try `rustc --explain E0502`.
error: could not compile `playground` due to previous error
In short, you can only have one borrow if you have a mutable reference, but the compiler is intelligent enough to know when the value cannot change: in your example, f cannot change when it is used before f2 is used, so it knows it doesn't change.
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
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.
I've read these docs: https://doc.rust-lang.org/rust-by-example/scope/borrow/mut.html
I've also read this question: (Cannot borrow immutable borrowed content as mutable)
The docs helped me understand how to declare borrowing as mutable (I think):
let mut (part1, part2) = someTuple;
But I'm haven't been able to find explicit instructions on what borrowing as an immutable looks like. This is my guess:
let (part1, part2) = someTuple;
I know this is a super basic question, but Googling it took me way off into the deep end of explanations and I'm still trying to get my bearings in the most simple of contexts.
How do I borrow as mutable vs an immutable in Rust?
let x = 0;
let immutable_borrow = &x; //borrow as immutable
//to borrow as mutable the variable needs to be declared as mutable
let mut y = 1;
let mutable_borrow = &mut y; //borrow as mutable
Note 1: you can borrow a variable either as immutable or mutable in the same scope, meaning you can't do this:
let mut x = 0;
let immutable_borrow = &x;
let mutable_borrow = &mut x;
Why?
Because if you would have mutable and immutable references of the same variable, then the data of that variable could change through that mutable reference and that could cause a lot of problems.
Note 2: you can immutably borrow a variable endless times but you can mutably borrow a variable only once.
//You can do this
let x = 0;
let x1 = &x;
let x2 = &x;
//...
//But you can't do this
let mut y = 0;
let y1 = &mut y;
let y2 = &mut y; //won't compile if you use y1 after you declare y2
Why?
As mentioned above. One mutable reference could change the data all the other mutable references are poiting to without them knowing. That could cause a lot of problems. But having multiple immutable references is okay, because the data can't be unexpectedly changed.
Ejdrien answers demonstrates the difference between mutable and immutable borrows, however it does not address a subtitle in your question, which is that you are performing borrows as part of pattern matching.
When you write
let (mut part1, mut part2) = someTuple;
you are binding the contents of someTuple to the mutable variables part1 and part2 by moving them. Unless the contents of someTuple were Copyable, the variables part1 and part2 become the exclusive owners of their respective values. If you attempt to access someTuple later by writing, e.g.
println!("{}", someTuple.0);
you'll receive a compile error from the borrow checker that resembles
error[E0382]: borrow of moved value: `someTuple.0`
--> main.rs:6:20
|
4 | let (mut part1, mut part2) = someTuple;
| --------- value moved here
5 |
6 | println!("{}", someTuple.0);
| ^^^^^^^^^^^ value borrowed here after move
In this particular context, there are two ways to inform the compiler that we want to only borrow the contents of someTuple. The first is the technique that Ejdrien described, which is explicitly borrowing the tuple and the performing the pattern matching against then resulting reference:
// Produce two mutable references
let (part1, part2) = &mut someTuple;
// Produce two immutable references
let (part1, part2) = &someTuple;
The problem with this approach is that we are forced to borrow everything in the same way. What if we only want a mutable reference to someTuple.0, and want to retrieve someTuple.1 as a copy, or as an immutable reference? For the tuple example here, this may not seem too critical, but for more complex cases of pattern matching, having this type of control is much more important.
This brings us two the second solution: binding references. Instead of the above, we can write
// Produce two mutable references
let (ref mut part1, ref mut part2) = someTuple;
// Produce two immutable references
let (ref part1, ref part2) = someTuple;
Here, we explicitly state how we want to bind each variable in the pattern matching. The key here is that we are free to intermix mutable and immutable borrows, so the following is also entirely valid:
// Produce immutable reference and one mutable reference
let (ref part1, ref mut part2) = someTuple;
println!("{}", &someTuple.0); // Make a second immutable reference someTuple.0
*part2 = ... // Mutate someTuple.1
println!("{}", part1); // Continue using the immutable reference
If we swap the above with an explicit mutable borrow on the right hand side, we'll once again receive errors from the borrow checker due to simultaneous mutable and immutable references:
let (part1, part2) = &mut someTuple;
println!("{}", &someTuple.0);
*part2 = ... // Mutate someTuple.1
println!("{}", part1);
produces
error[E0502]: cannot borrow `someTuple.0` as immutable because it is also borrowed as mutable
--> main.rs:6:20
|
4 | let (part1,part2) =&mut someTuple;
| -------------- mutable borrow occurs here
5 |
6 | println!("{}", &someTuple.0);
| ^^^^^^^^^^^^ immutable borrow occurs here
...
9 | println!("{}", part1);
| ----- mutable borrow later used here
error: aborting due to previous error