How borrow as mutable vs immutable in Rust? - rust

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

Related

Rust - Why does dereferencing and borrowing break borrowing rules?

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.

Nested method calls with existing mutable references

The following code compiles successfully:
let mut v = vec![1];
let r = &mut v;
r.push(r.len());
while this one fails:
let mut v = vec![1];
let r = &mut v;
r.push(v.len());
with error:
error[E0502]: cannot borrow `v` as immutable because it is also borrowed as mutable
|
| let r = &mut v;
| ------ mutable borrow occurs here
| r.push(v.len());
| ^ immutable borrow occurs here
| r.push(r.len());
| - mutable borrow later used here
Why the first example compiles correctly? Is it because the same reference: r is used in the outer and inner calls? Or is it because it applies the RFC 2025, Two-Phase Borrows? Or something else?
Why the second example fails given that the first example succeeds? Why the RFC 2025, Two-Phase Borrows does not apply here?
I suspect that in the first example there are no errors because the compiler does not create intermediate references and it uses the same reference: r so that there are not multiple borrows.
However, if that is the case, why the following code fails to compile
let mut v = vec![1];
let r = &mut v;
r.push({r.push(0);1});
In the second example, v is still mutably borrowed when you try to get its length. Vec::len takes &self, so getting its length would mean borrowing immutably while it's already borrowed mutably.
Accessing len() through r is still ok because you are borrowing through the same borrow, not borrowing it again.
Note that even the first example fails in Rust 1.30 and earlier (or 1.35 with 2015 edition), because it relies on NLL (non-lexical lifetimes). The problem with NLL is that it isn't completely intuitive to know what is allowed. Essentially, it means that borrows that don't outlive the lexical scope of the data are ok, and also several other intuitively correct cases. Your third example is one of the cases that is still not permited by NLL.
1st example:
let mut v = vec![1];
let r = &mut v;
r.push(r.len());
Without the 2-phase borrows the code will not compile because the outer call creates a reborrow of r: &mut *r, and the inner call a new immutable reborrow of the same value: &*r.
With 2-phase borrows, the first reborrow is converted to &mut2 *r and later activated when the second reborrow is out of scope.
2nd example:
let mut v = vec![1];
let r = &mut v;
r.push(v.len());
Even with the 2-phase borrows it does not compile.
The inner call causes a reborrow of v: &mut v that clashes with r.
3rd example:
let mut v = vec![1];
let r = &mut v;
r.push({r.push(0);0});
Even with the 2-phase borrows it does not compile.
The inner call requires a &mut2 reborrows of *r that is not allowed by the 2-phase borrows since the outer call already created a &mut2 reborrows of *r.
References:
https://angelocatalani.github.io/2020-12-29-References-and-borrowing-in-Rust/
https://users.rust-lang.org/t/nested-method-calls-with-existing-mutable-references/53345
https://rust-lang.github.io/rfcs/2025-nested-method-calls.html
Why does re-borrowing only work on de-referenced pointers?

Understanding immutable borrows in loops

I can't quite understand why this compiles:
fn main() {
let mut v = vec![1,2,3];
for i in 1..v.len() {
v[i] = 20;
}
}
...and this doesn't:
fn main() {
let mut v = vec![1,2,3];
for (i,_) in v.iter().enumerate() {
v[i] = 20;
}
}
Error:
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
--> src/main.rs:6:13
|
4 | for (i,_) in v.iter().enumerate() {
| --------------------
| |
| immutable borrow occurs here
| immutable borrow later used here
5 |
6 | v[i] = 20;
| ^ mutable borrow occurs here
In both cases we make an immutable borrow (one when call len(), other when we call iter()).
Thus, my expectation was that the 1st snippet should NOT compile -- we're making an mutable borrow when doing the assignment, when an immutable borrow exists.
What am I misunderstanding?
You are not actually making an immutable borrow in the first case, or rather, it ends after the call to len() returns (as len returns a primitive type not holding a reference to what it was used on). This means that your loop is perfectly fine, since you hold the one and only mutable object.
On the second one, you are creating a type that implements Iterator<Item = &u32> and then iterating on that iterator. The iterator has an immutable borrow to your collection (how else could you call next() on it otherwise?). This is a bit hidden, but that's where the immutable borrow is and why you cannot do what you did.
Typically, when working with iterators, and when you need to modify an element being iterated on, iter_mut is the way to go, for obvious reasons :-)

Mutable borrow automatically changes to immutable?

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 t
v: an immutable binding, which is an immutable re-borrow of t through u
If, 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 t
v: an immutable binding, which is a mutable re-borrow of t through u
The 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:
a re-borrow access the initially borrowed variable
for the lifetime of the re-borrow, the reference from which it is formed is borrowed
The re-borrowing rules are relatively simple, they mirror the borrowing rules:
if you start from an immutable reference:
you can re-borrow it only as an immutable reference, with multiple concurrent immutable re-borrow if you wish
if you start from a mutable reference:
you can either re-borrow it as a mutable reference, exclusively
or you can re-borrow it as an immutable reference, with multiple concurrent immutable re-borrow if you wish
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 t as 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 t as 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 *u as 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.
The other key thing is that you can only use mutable values if the full path to the item is mutable. Since 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...)
First of all, u is not mutable at any point, as it was declared with let u, not let mut u. The reason why you can mutate the String it points to is that it holds a mutable reference to it; make_ascii_uppercase() modifies t.
v is also immutable (no mut in let v), so when you call show() that works on immutable references, the borrowing rules are not violated - you can perform multiple immutable borrows at once.

Why does Rust want to borrow a variable as mutable more than once at a time?

I'm attempting to implement a dynamic programming problem in Rust to gain familiarity with the language. Like many dynamic programming problems, this uses memoization to reduce the running time. Unfortunately, my first-pass solution yields errors. I've pared the code down to the following. Warning - it's now a bit nonsensical:
use std::collections::HashMap;
fn repro<'m>(memo: &'m mut HashMap<i32, Vec<i32>>) -> Option<&'m Vec<i32>> {
{
let script_a = repro(memo);
let script_b = repro(memo);
}
memo.get(&0)
}
fn main() {}
The compilation error is:
error[E0499]: cannot borrow `*memo` as mutable more than once at a time
--> src/main.rs:6:30
|
5 | let script_a = repro(memo);
| ---- first mutable borrow occurs here
6 | let script_b = repro(memo);
| ^^^^ second mutable borrow occurs here
7 | }
| - first borrow ends here
Why is the variable memo borrowed multiple times? In my view, it should be borrowed once when I compute script_a, then that borrow ends, then it gets borrowed again for script_b.
Currently borrows last for the block they are defined in (#9113 might change this if implemented)
The problem is that script_a (that holds an immutable reference to a map) is valid for the whole block and you try to use a mutable reference to the same map:
let script_a = repro(memo);
let script_b = repro(memo);
// script_a is still alive
The bigger problem is the infinite loop. Anyway let script_a is a reference to data inside the hash map, so it's still borrowed by the time you call let script_b = repro(memo);.

Resources