It seems that shadowing a variable does not release the borrowed reference it holds. The following code does not compile:
fn main() {
let mut a = 40;
let r1 = &mut a;
let r1 = "shadowed";
let r2 = &mut a;
}
With the error message:
error[E0499]: cannot borrow `a` as mutable more than once at a time
--> src/main.rs:5:19
|
3 | let r1 = &mut a;
| - first mutable borrow occurs here
4 | let r1 = "shadowed";
5 | let r2 = &mut a;
| ^ second mutable borrow occurs here
6 | }
| - first borrow ends here
I would expect the code to compile because the first reference r1 is shadowed before borrowing the second reference r2. Obviously, the first borrow lives until the end of the block although it is no longer accessible after line 4. Why is that the case?
TL;DR: Shadowing is about name-lookup, borrowing is about lifetimes.
From a compiler point of view, variables have no name:
fn main() {
let mut __0 = 40;
let __1 = &mut __0;
let __2 = "shadowed";
let __3 = &mut __0;
}
This is not very readable for a human being, so the language allows us to use descriptive names instead.
Shadowing is an allowance on reusing names, which for the lexical scope of the "shadowing" variable will resolve the name to the "shadowing" one (__2 here) instead of the "original" one (__1 here).
However just because the old one can no longer be accessed does not mean it no longer lives: Shadowing != Assignment. This is especially notable with different scopes:
fn main() {
let i = 3;
for i in 0..10 {
}
println!("{}", i);
}
Will always print 3: once the shadowing variable's scope ends, the name resolves to the original again!
It's not like the original r1 ceases to exist after it becomes shadowed; consider the MIR produced for your code without the last line (r2 binding):
fn main() -> () {
let mut _0: (); // return pointer
scope 1 {
let mut _1: i32; // "a" in scope 1 at src/main.rs:2:9: 2:14
scope 2 {
let _2: &mut i32; // "r1" in scope 2 at src/main.rs:3:9: 3:11
scope 3 {
let _3: &str; // "r1" in scope 3 at src/main.rs:4:9: 4:11
}
}
}
bb0: {
StorageLive(_1); // scope 0 at src/main.rs:2:9: 2:14
_1 = const 40i32; // scope 0 at src/main.rs:2:17: 2:19
StorageLive(_2); // scope 1 at src/main.rs:3:9: 3:11
_2 = &mut _1; // scope 1 at src/main.rs:3:14: 3:20
StorageLive(_3); // scope 2 at src/main.rs:4:9: 4:11
_3 = const "shadowed"; // scope 2 at src/main.rs:4:14: 4:24
_0 = (); // scope 3 at src/main.rs:1:11: 5:2
StorageDead(_3); // scope 2 at src/main.rs:5:2: 5:2
StorageDead(_2); // scope 1 at src/main.rs:5:2: 5:2
StorageDead(_1); // scope 0 at src/main.rs:5:2: 5:2
return; // scope 0 at src/main.rs:5:2: 5:2
}
}
Note that when "shadowed" becomes bound (_3), it doesn't change anything related to the original r1 binding (_2); the name r1 no longer applies to the mutable reference, but the original variable still exists.
I wouldn't consider your example a very useful case of shadowing; its usual applications, e.g. bodies of loops, are much more likely to utilize it.
Related
I want to use a double For loop to compare each element with each and if these properties are the same, make a change to the objects. Below is a little demo code of what I want to do.
But the Rust compiler tells me here that I can't have 2 mutable references from the same object. How can I implement the whole thing differently?
fn main() {
struct S {
a: i32
}
let mut v = vec![S{a: 1}, S{a: 1}, S{a: 1}];
let size = v.len();
for i in 0..size {
for j in 0..size {
if i == j {
continue;
}
let a = &mut v[i];
let b = &mut v[j];
if a.a == b.a {
a.a = 5;
}
}
}
}
Console:
error[E0499]: cannot borrow `v` as mutable more than once at a time
--> src\main.rs:35:26
|
34 | let a = &mut v[i];
| - first mutable borrow occurs here
35 | let b = &mut v[j];
| ^ second mutable borrow occurs here
36 |
37 | if a.a == b.a {
| --- first borrow later used here
First thing to note is that you don't need two mutable references since you are only mutating v[i].
Writing it like this will create two immutable borrows and inside the if a mutable borrow:
if v[i].a == v[j].a {
v[i].a = 5;
}
Being very explicit this is what is happening (but its nicer the other way):
let a = &v[i];
let b = &v[j];
if a.a == b.a {
let mut_a = &mut v[i];
mut_a.a = 5;
}
One of the rules of the borrow checker is that no two mutable borrows can exist to the same thing at the same time.
Consider the following (contrived) way to increment x by 9.
fn main() {
let mut x = 0;
let mut f = || {
x += 4;
};
let _g = || {
f();
x += 5;
};
}
error[E0499]: cannot borrow `x` as mutable more than once at a time
--> x.rs:6:12
|
3 | let mut f = || {
| -- first mutable borrow occurs here
4 | x += 4;
| - first borrow occurs due to use of `x` in closure
5 | };
6 | let _g = || {
| ^^ second mutable borrow occurs here
7 | f();
| - first borrow later captured here by closure
8 | x += 5;
| - second borrow occurs due to use of `x` in closure
error: aborting due to previous error
For more information about this error, try `rustc --explain E0499`.
So, it does not work. How to make an algorithm like the above that would modify a variable in a closure and call another closure from it that also modifies the variable?
In traditional languages it's easy. What to do in Rust?
The accepted answer is the most idiomatic approach, but there is also an alternative that is useful in situations when the additional argument doesn't work, for example when you need to pass the closure to third-party code that will call it without arguments. In that case you can use a Cell, a form of interior mutability:
use std::cell::Cell;
fn main() {
let x = Cell::new(0);
let f = || {
x.set(x.get() + 4);
};
let g = || {
f();
x.set(x.get() + 5);
};
f();
g();
assert_eq!(x.get(), 13);
}
By design, closures have to enclose external objects that are used in it upon creation, in our case closures have to borrow the external x object. So as compiler explained to you, upon creation of the closure f it mutably borrows x, and you can't borrow it once again when you create closure g.
In order to compile it you can't enclose any external object that you want to change. Instead you can directly pass the objects as a closure's argument (arguably it's even more readable). This way you describe that a closure accepts some object of some type, but you don't yet use/pass any actual object. This object will be borrowed only when you call the closure.
fn main() {
let mut x = 0;
let f = |local_x: &mut i32| { // we don't enclose `x`, so no borrow yet
*local_x += 4;
};
let _g = |local_x: &mut i32| { // we don't enclose `x`, so no borrow yet
f(local_x);
*local_x += 5;
};
_g(&mut x); // finally we borrow `x`, and this borrow will later move to `f`,
// so no simultaneous borrowing.
println!("{}", x); // 9
}
To expand on Alex Larionov's answer: you should think of a closure as a callable structure, anything you capture is set as a field of the structure, then implicitly dereferenced inside the function body. The way those fields are used is also what determines whether the closure is Fn, FnMut or FnOnce, basically whether the method would take &self, &mut self or self if it were written longhand.
Here
fn main() {
let mut x = 0;
let mut f = || {
x += 4;
};
// ...
}
essentially translates to:
struct F<'a> { x: &'a mut u32 }
impl F<'_> {
fn call(&mut self) {
*self.x += 4
}
}
fn main() {
let mut x = 0;
let mut f = F { x: &mut x };
// ...
}
from this, you can see that as soon as f is created, x is mutably borrowed, with all that implies.
And with this partial desugaring, we can see essentially the same error: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=e129f19f25dc61de8a6f42cdca1f67b5
If we use the relevant unstable features on nightly we can get just a bit closer. That is pretty much what rustc does for us under the hood.
a is a Vec<i32> which can be mutably and immutably referenced in one expression:
fn main() {
let mut a = vec![0, 1];
a[0] += a[1]; // OK
}
I thought this compiled because i32 implements Copy, so I created another type that implements Copy and compiled it like the first example, but it fails:
use std::ops::AddAssign;
#[derive(Clone, Copy, PartialEq, Debug, Default)]
struct MyNum(i32);
impl AddAssign for MyNum {
fn add_assign(&mut self, rhs: MyNum) {
*self = MyNum(self.0 + rhs.0)
}
}
fn main() {
let mut b = vec![MyNum(0), MyNum(1)];
b[0] += b[1];
}
playground
error[E0502]: cannot borrow `b` as immutable because it is also borrowed as mutable
--> src/main.rs:14:13
|
14 | b[0] += b[1];
| --------^---
| | |
| | immutable borrow occurs here
| mutable borrow occurs here
| mutable borrow later used here
Why does my MyNum not behave in the same way as i32 even though it implements Copy?
Why can the vector be mutably and immutably referenced in one expression?
I believe the thing you're seeing here is that primitive types do not actually call their std::ops equivalents. Those std::ops may just be included for seamless trait extensions, etc. I think the blog post Rust Tidbits: What Is a Lang Item? partially explains this.
I exported the MIR of your example that works with primitive types. I got:
bb5: {
StorageDead(_9); // bb5[0]: scope 1 at src/main.rs:6:8: 6:9
_10 = CheckedAdd((*_8), move _5); // bb5[1]: scope 1 at src/main.rs:6:5: 6:17
assert(!move (_10.1: bool), "attempt to add with overflow") -> [success: bb6, unwind: bb4]; // bb5[2]: scope 1 at src/main.rs:6:5: 6:17
}
I had a lot of difficulty exporting the MIR for the code that was erroring. Outputting MIR without borrow checking is new to me and I couldn't figure out how to do it.
This playground has a very similar thing, but compiles :)
It gives me an actual call to add_assign:
bb3: {
_8 = _9; // bb3[0]: scope 1 at src/main.rs:14:5: 14:9
StorageDead(_10); // bb3[1]: scope 1 at src/main.rs:14:8: 14:9
StorageLive(_11); // bb3[2]: scope 1 at src/main.rs:14:14: 14:22
(_11.0: i32) = const 1i32; // bb3[3]: scope 1 at src/main.rs:14:14: 14:22
// ty::Const
// + ty: i32
// + val: Value(Scalar(0x00000001))
// mir::Constant
// + span: src/main.rs:14:20: 14:21
// + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) }
_7 = const <MyNum as std::ops::AddAssign>::add_assign(move _8, move _11) -> [return: bb5, unwind: bb4]; // bb3[4]: scope 1 at src/main.rs:14:5: 14:22
// ty::Const
// + ty: for<'r> fn(&'r mut MyNum, MyNum) {<MyNum as std::ops::AddAssign>::add_assign}
// + val: Value(Scalar(<ZST>))
// mir::Constant
// + span: src/main.rs:14:5: 14:22
// + literal: Const { ty: for<'r> fn(&'r mut MyNum, MyNum) {<MyNum as std::ops::AddAssign>::add_assign}, val: Value(Scalar(<ZST>)) }
}
How does the primitive case pass the borrow checker? Since add_assign is not called, the immutable reference can be dropped before the mutable reference is required. The MIR simply dereferences the needed location earlier on and passes it through by value.
bb3: {
_5 = (*_6); // bb3[0]: scope 1 at src/main.rs:6:13: 6:17
StorageDead(_7); // bb3[1]: scope 1 at src/main.rs:6:16: 6:17
...
}
Here is the code:
fn test(){
let mut numbers = vec![2];
let f = || {
for _ in numbers.iter(){
}
false
};
while false {
let res = f();
if res {
numbers.push(10);
}
}
}
The error is:
|
15 | let f = || {
| -- immutable borrow occurs here
16 | for _ in numbers.iter(){
| ------- first borrow occurs due to use of `numbers` in closure
...
22 | let res = f();
| - immutable borrow later used here
23 | if res {
24 | numbers.push(10);
| ^^^^^^^^^^^^^^^^ mutable borrow occurs here
But if I change the while keyword to if, it can be compiled. How to fix this? I want to call the anonymous function in a loop.
We can simplify your example even more by replacing the closure by a simple immutable reference
let mut numbers = vec![2];
let r = &numbers;
while false {
println!("{:?}", r);
numbers.push(10);
}
Here we get this error:
error[E0502]: cannot borrow `numbers` as mutable because it is also borrowed as immutable
--> src/lib.rs:7:5
|
3 | let r = &numbers;
| -------- immutable borrow occurs here
...
6 | println!("{:?}", r); // use reference
| - immutable borrow later used here
7 | numbers.push(10);
| ^^^^^^^^^^^^^^^^ mutable borrow occurs here
And like in your example, replacing the while with if makes the error go away. Why?
You probably know about the important Rust rule: Aliasing nand mutability. It states that, at any given time, a value can either be borrowed immutably arbitrarily many times or mutably exactly once.
The statement numbers.push(10) borrows numbers mutably temporarily (just for the statement). But we also have r which is an immutable reference. In order for numbers.push(10) to work, the compiler has to make sure that no other borrow exists at that time. But there exists the reference r! This reference cannot exist at the same time as numbers.push(10) exists.
Let's see for the if case first:
let mut numbers = vec![2];
let r = &numbers; // <------+ (creation of r)
// |
if false { // |
println!("{:?}", r); // <------+ (last use of r)
numbers.push(10);
}
While the lexical scope means the variable r is only dropped at the end of the function, due to non-lexical lifetimes, the compiler can see that the last use of r is in the println line. Then the compiler can mark r as "dead" after this line. And this in turn means, that there is no other borrow in the line numbers.push(10) and everything works out fine.
And for the loop case? Let's imagine the loop iterating three times:
let mut numbers = vec![2];
let r = &numbers; // <------+ (creation of r)
// |
// First iteration // |
println!("{:?}", r); // |
numbers.push(10); // | <== oh oh!
// |
// Second iteration // |
println!("{:?}", r); // |
numbers.push(10); // |
// |
// Third iteration // |
println!("{:?}", r); // <------+ (last use of r)
numbers.push(10);
As can be seen here, the time in which r is active overlaps numbers.push(10) (except the last one). And as a result, the compiler will produce an error because this code violates the central Rust rule.
And the explanation is the same for your closure case: the closure borrows numbers immutably and f(); uses that closure. In the loop case, the compiler is not able to shrink the "alive time" of the closure enough to make sure it doesn't overlap the mutable borrow for push.
How to fix?
Well, you could pass numbers into the closure each time:
let mut numbers = vec![2];
let f = |numbers: &[i32]| {
for _ in numbers.iter(){
}
false
};
while false {
let res = f(&numbers);
if res {
numbers.push(10);
}
}
This works because now, numbers is borrowed immutably also just temporarily for the f(&numbers); statement.
You can also use a RefCell as the other answer suggested, but that should be a last resort.
It's not exactly sure what you're trying to accomplish, but one way to solve this, without changing your code too drastically, would be to use std::cell::RefCell (described in the std and in the book):
use std::cell::RefCell;
fn test(){
let numbers = RefCell::new(vec![2]);
let f = || {
for _ in numbers.borrow().iter(){
}
false
};
while false {
let res = f();
if res {
numbers.borrow_mut().push(10);
}
}
}
Here's a little bit tweaked demo, which actually does something:
use std::cell::RefCell;
fn main() {
test();
}
fn test() {
let numbers = RefCell::new(vec![0]);
let f = || {
for n in numbers.borrow().iter() {
println!("In closure: {}", n);
}
println!();
true
};
let mut i = 1;
while i <= 3 {
let res = f();
if res {
numbers.borrow_mut().push(i);
}
i += 1;
}
println!("End of test(): {:?}", numbers.borrow());
}
Output:
In closure: 0
In closure: 0
In closure: 1
In closure: 0
In closure: 1
In closure: 2
End of test(): [0, 1, 2, 3]
Rust Playground demo
Given this:
fn main() {
let variable = [0; 15];
}
The Rust compiler produces this warning:
= note: #[warn(unused_variables)] on by default
= note: to avoid this warning, consider using `_variable` instead
What's the difference between variable and _variable?
The difference is an underscore at the front, which causes the Rust compiler to allow it to be unused. It is kind of a named version of the bare underscore _ which can be used to ignore a value.
However, _name acts differently than _. The plain underscore drops the value immediately while _name acts like any other variable and drops the value at the end of the scope.
An example of how it does not act exactly the same as a plain underscore:
struct Count(i32);
impl Drop for Count {
fn drop(&mut self) {
println!("dropping count {}", self.0);
}
}
fn main() {
{
let _a = Count(3);
let _ = Count(2);
let _c = Count(1);
}
{
let _a = Count(3);
let _b = Count(2);
let _c = Count(1);
}
}
prints the following (playground):
dropping count 2
dropping count 1
dropping count 3
dropping count 1
dropping count 2
dropping count 3
The key difference between _variable and variable is that first one tells compiler not to give any warnings if we do not use it in our code. Example:
// src/main.rs
fn main() {
let _x = 1;
let y = 2;
}
Compiling main.rs gives:
warning: unused variable: `y`
--> src/main.rs:3:9
|
3 | let y = 2;
| ^ help: if this is intentional, prefix it with an underscore: `_y`
|
= note: `#[warn(unused_variables)]` on by default
The more interesting case is when we are comparing _ with _variable.
Ignoring an Unused Variable by Starting Its Name with _:
The syntax _x still binds the value to the variable, whereas _ doesn’t bind at all.
Consider example:
// src/main.rs
fn main() {
let s = Some(String::from("Hello!"));
if let Some(_s) = s {
println!("found a string");
}
println!("{:?}", s);
}
When we try to compile main.rs we get error:
error[E0382]: borrow of moved value: `s`
--> src/main.rs:8:22
|
4 | if let Some(_s) = s {
| -- value moved here
...
8 | println!("{:?}", s);
| ^ value borrowed here after partial move
|
= note: move occurs because value has type `std::string::String`, which does not implement the `Copy` trait
help: borrow this field in the pattern to avoid moving `s.0`
|
4 | if let Some(ref _s) = s {
| ^^^
Aha! The syntax _x still binds the value to the variable, which means that we are moving the ownership of s to _s, thus, we can no longer access variable s anymore; which happens when we try to print value of s.
The correct way of doing the above is:
// src/main.rs
fn main() {
let s = Some(String::from("Hello!"));
if let Some(_) = s {
println!("found a string");
}
println!("{:?}", s);
}
Above code works just fine. s does not get moved into _, so we can still access it later.
Sometimes I use _ with iterators:
fn main() {
let v = vec![1, 2, 3];
let _ = v
.iter()
.map(|x| {
println!("{}", x);
})
.collect::<Vec<_>>();
}
Compiling gives result:
1
2
3
When doing more complex operations on iterable types above example acts as utility for me.