Why are borrows not allowed in mutable function's argument [duplicate] - rust

What is going on here (playground)?
struct Number {
num: i32
}
impl Number {
fn set(&mut self, new_num: i32) {
self.num = new_num;
}
fn get(&self) -> i32 {
self.num
}
}
fn main() {
let mut n = Number{ num: 0 };
n.set(n.get() + 1);
}
Gives this error:
error[E0502]: cannot borrow `n` as immutable because it is also borrowed as mutable
--> <anon>:17:11
|
17 | n.set(n.get() + 1);
| - ^ - mutable borrow ends here
| | |
| | immutable borrow occurs here
| mutable borrow occurs here
However if you simply change the code to this it works:
fn main() {
let mut n = Number{ num: 0 };
let tmp = n.get() + 1;
n.set(tmp);
}
To me those look exactly equivalent - I mean, I would expect the former to be transformed to the latter during compilation. Doesn't Rust evaluate all function parameters before evaluating the next-level-up function call?

This line:
n.set(n.get() + 1);
is desugared into
Number::set(&mut n, n.get() + 1);
The error message might be a bit more clear now:
error[E0502]: cannot borrow `n` as immutable because it is also borrowed as mutable
--> <anon>:18:25
|
18 | Number::set(&mut n, n.get() + 1);
| - ^ - mutable borrow ends here
| | |
| | immutable borrow occurs here
| mutable borrow occurs here
As Rust evaluates arguments left to right, that code is equivalent to this:
let arg1 = &mut n;
let arg2 = n.get() + 1;
Number::set(arg1, arg2);
Editor's note: This code example gives an intuitive sense of the underlying problem, but isn't completely accurate. The expanded code still fails even with non-lexical lifetimes, but the original code compiles. For the full description of the problem, review the comments in the original implementation of the borrow checker
It should now be obvious what is wrong. Swapping those first two lines fixes this, but Rust does not do that kind of control-flow analysis.
This was first created as bug #6268, now it is integrated into RFC 2094, non-lexical-lifetimes. If you use Rust 1.36 or newer, NLL is enabled automatically and your code will now compile without an error.
See also:
Why is a mutable borrow in an argument disallowed for a mutable method call?

Related

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)

Is there a good pattern for passing an argument using self immutably to a method using self mutably? [duplicate]

What is going on here (playground)?
struct Number {
num: i32
}
impl Number {
fn set(&mut self, new_num: i32) {
self.num = new_num;
}
fn get(&self) -> i32 {
self.num
}
}
fn main() {
let mut n = Number{ num: 0 };
n.set(n.get() + 1);
}
Gives this error:
error[E0502]: cannot borrow `n` as immutable because it is also borrowed as mutable
--> <anon>:17:11
|
17 | n.set(n.get() + 1);
| - ^ - mutable borrow ends here
| | |
| | immutable borrow occurs here
| mutable borrow occurs here
However if you simply change the code to this it works:
fn main() {
let mut n = Number{ num: 0 };
let tmp = n.get() + 1;
n.set(tmp);
}
To me those look exactly equivalent - I mean, I would expect the former to be transformed to the latter during compilation. Doesn't Rust evaluate all function parameters before evaluating the next-level-up function call?
This line:
n.set(n.get() + 1);
is desugared into
Number::set(&mut n, n.get() + 1);
The error message might be a bit more clear now:
error[E0502]: cannot borrow `n` as immutable because it is also borrowed as mutable
--> <anon>:18:25
|
18 | Number::set(&mut n, n.get() + 1);
| - ^ - mutable borrow ends here
| | |
| | immutable borrow occurs here
| mutable borrow occurs here
As Rust evaluates arguments left to right, that code is equivalent to this:
let arg1 = &mut n;
let arg2 = n.get() + 1;
Number::set(arg1, arg2);
Editor's note: This code example gives an intuitive sense of the underlying problem, but isn't completely accurate. The expanded code still fails even with non-lexical lifetimes, but the original code compiles. For the full description of the problem, review the comments in the original implementation of the borrow checker
It should now be obvious what is wrong. Swapping those first two lines fixes this, but Rust does not do that kind of control-flow analysis.
This was first created as bug #6268, now it is integrated into RFC 2094, non-lexical-lifetimes. If you use Rust 1.36 or newer, NLL is enabled automatically and your code will now compile without an error.
See also:
Why is a mutable borrow in an argument disallowed for a mutable method call?

Multiple Calls on a Single method in rust [duplicate]

What is going on here (playground)?
struct Number {
num: i32
}
impl Number {
fn set(&mut self, new_num: i32) {
self.num = new_num;
}
fn get(&self) -> i32 {
self.num
}
}
fn main() {
let mut n = Number{ num: 0 };
n.set(n.get() + 1);
}
Gives this error:
error[E0502]: cannot borrow `n` as immutable because it is also borrowed as mutable
--> <anon>:17:11
|
17 | n.set(n.get() + 1);
| - ^ - mutable borrow ends here
| | |
| | immutable borrow occurs here
| mutable borrow occurs here
However if you simply change the code to this it works:
fn main() {
let mut n = Number{ num: 0 };
let tmp = n.get() + 1;
n.set(tmp);
}
To me those look exactly equivalent - I mean, I would expect the former to be transformed to the latter during compilation. Doesn't Rust evaluate all function parameters before evaluating the next-level-up function call?
This line:
n.set(n.get() + 1);
is desugared into
Number::set(&mut n, n.get() + 1);
The error message might be a bit more clear now:
error[E0502]: cannot borrow `n` as immutable because it is also borrowed as mutable
--> <anon>:18:25
|
18 | Number::set(&mut n, n.get() + 1);
| - ^ - mutable borrow ends here
| | |
| | immutable borrow occurs here
| mutable borrow occurs here
As Rust evaluates arguments left to right, that code is equivalent to this:
let arg1 = &mut n;
let arg2 = n.get() + 1;
Number::set(arg1, arg2);
Editor's note: This code example gives an intuitive sense of the underlying problem, but isn't completely accurate. The expanded code still fails even with non-lexical lifetimes, but the original code compiles. For the full description of the problem, review the comments in the original implementation of the borrow checker
It should now be obvious what is wrong. Swapping those first two lines fixes this, but Rust does not do that kind of control-flow analysis.
This was first created as bug #6268, now it is integrated into RFC 2094, non-lexical-lifetimes. If you use Rust 1.36 or newer, NLL is enabled automatically and your code will now compile without an error.
See also:
Why is a mutable borrow in an argument disallowed for a mutable method call?

How can I pass an element of a vector as argument for `.push()` into the same vector without introduction of a temporary variable? [duplicate]

What is going on here (playground)?
struct Number {
num: i32
}
impl Number {
fn set(&mut self, new_num: i32) {
self.num = new_num;
}
fn get(&self) -> i32 {
self.num
}
}
fn main() {
let mut n = Number{ num: 0 };
n.set(n.get() + 1);
}
Gives this error:
error[E0502]: cannot borrow `n` as immutable because it is also borrowed as mutable
--> <anon>:17:11
|
17 | n.set(n.get() + 1);
| - ^ - mutable borrow ends here
| | |
| | immutable borrow occurs here
| mutable borrow occurs here
However if you simply change the code to this it works:
fn main() {
let mut n = Number{ num: 0 };
let tmp = n.get() + 1;
n.set(tmp);
}
To me those look exactly equivalent - I mean, I would expect the former to be transformed to the latter during compilation. Doesn't Rust evaluate all function parameters before evaluating the next-level-up function call?
This line:
n.set(n.get() + 1);
is desugared into
Number::set(&mut n, n.get() + 1);
The error message might be a bit more clear now:
error[E0502]: cannot borrow `n` as immutable because it is also borrowed as mutable
--> <anon>:18:25
|
18 | Number::set(&mut n, n.get() + 1);
| - ^ - mutable borrow ends here
| | |
| | immutable borrow occurs here
| mutable borrow occurs here
As Rust evaluates arguments left to right, that code is equivalent to this:
let arg1 = &mut n;
let arg2 = n.get() + 1;
Number::set(arg1, arg2);
Editor's note: This code example gives an intuitive sense of the underlying problem, but isn't completely accurate. The expanded code still fails even with non-lexical lifetimes, but the original code compiles. For the full description of the problem, review the comments in the original implementation of the borrow checker
It should now be obvious what is wrong. Swapping those first two lines fixes this, but Rust does not do that kind of control-flow analysis.
This was first created as bug #6268, now it is integrated into RFC 2094, non-lexical-lifetimes. If you use Rust 1.36 or newer, NLL is enabled automatically and your code will now compile without an error.
See also:
Why is a mutable borrow in an argument disallowed for a mutable method call?

Cannot borrow as immutable because it is also borrowed as mutable in function arguments

What is going on here (playground)?
struct Number {
num: i32
}
impl Number {
fn set(&mut self, new_num: i32) {
self.num = new_num;
}
fn get(&self) -> i32 {
self.num
}
}
fn main() {
let mut n = Number{ num: 0 };
n.set(n.get() + 1);
}
Gives this error:
error[E0502]: cannot borrow `n` as immutable because it is also borrowed as mutable
--> <anon>:17:11
|
17 | n.set(n.get() + 1);
| - ^ - mutable borrow ends here
| | |
| | immutable borrow occurs here
| mutable borrow occurs here
However if you simply change the code to this it works:
fn main() {
let mut n = Number{ num: 0 };
let tmp = n.get() + 1;
n.set(tmp);
}
To me those look exactly equivalent - I mean, I would expect the former to be transformed to the latter during compilation. Doesn't Rust evaluate all function parameters before evaluating the next-level-up function call?
This line:
n.set(n.get() + 1);
is desugared into
Number::set(&mut n, n.get() + 1);
The error message might be a bit more clear now:
error[E0502]: cannot borrow `n` as immutable because it is also borrowed as mutable
--> <anon>:18:25
|
18 | Number::set(&mut n, n.get() + 1);
| - ^ - mutable borrow ends here
| | |
| | immutable borrow occurs here
| mutable borrow occurs here
As Rust evaluates arguments left to right, that code is equivalent to this:
let arg1 = &mut n;
let arg2 = n.get() + 1;
Number::set(arg1, arg2);
Editor's note: This code example gives an intuitive sense of the underlying problem, but isn't completely accurate. The expanded code still fails even with non-lexical lifetimes, but the original code compiles. For the full description of the problem, review the comments in the original implementation of the borrow checker
It should now be obvious what is wrong. Swapping those first two lines fixes this, but Rust does not do that kind of control-flow analysis.
This was first created as bug #6268, now it is integrated into RFC 2094, non-lexical-lifetimes. If you use Rust 1.36 or newer, NLL is enabled automatically and your code will now compile without an error.
See also:
Why is a mutable borrow in an argument disallowed for a mutable method call?

Resources