The following code produces interesting results:
use std::thread;
fn main() {
let mut handles = Vec::new();
let mut v = vec![1, 2, 3, 4];
for x in v.chunks_exact_mut(2) {
let handle = thread::spawn(move || {
println!("Here's a vector: {:?}", x);
});
handles.push(handle);
}
for h in handles { h.join().unwrap(); }
}
error[E0597]: `v` does not live long enough
--> src/main.rs:7:14
|
7 | for x in v.chunks_exact_mut(2) {
| ^^^^^^^^^^^^^^^^^^^^^
| |
| borrowed value does not live long enough
| argument requires that `v` is borrowed for `'static`
...
16 | }
| - `v` dropped here while still borrowed
Why does 'chunking' v change the lifetime? Without 'chunking' v, the code performs correctly. So why now does it throw an error?
The problem here is that thread::spawn completely decouples the lifetime from the rest, as the thread might continue to run while main is already finished.
x is a reference to a variable in main, and there is no guarantee that main lives longer than the thread.
I think what you really want here is a threading helper library like rayon, as they already solve those problems internally:
use rayon::prelude::*;
fn main() {
let mut v = vec![1, 2, 3, 4];
v.par_chunks_exact_mut(2).for_each(|x: &mut [i32]| {
let thread_id = std::thread::current().id();
println!("{:?}: Here's a vector: {:?}", thread_id, x);
});
}
ThreadId(5): Here's a vector: [1, 2]
ThreadId(2): Here's a vector: [3, 4]
Without "chunking" v, you're iterating over Vec<i32>. This iterator produces items of type i32, so, no references, no lifetimes, the requirements for thread::spawn() (specifically the 'static requirement) are met.
chunks_exact_mut(), however, is defined on slices and gives iterator over &mut [T], so it does have a reference. And since this reference refers v, which exists in main(), it is not 'static and does not work.
You can observe the same problem if you'll iterate over &v instead of v, since that iterator gives you &i32s (playground):
error[E0597]: `v` does not live long enough
--> src/main.rs:7:14
|
7 | for x in &v {
| ^^
| |
| borrowed value does not live long enough
| argument requires that `v` is borrowed for `'static`
...
16 | }
| - `v` dropped here while still borrowed
Other than that, I'd recommend using rayon as #Finomnis suggested.
Related
In the context of a crypto problem, to decrease verbosity, I'm "naming" a slice of v as block, doing some stuff to it, but then trying to verify the result of do_stuff on the whole of v:
fn x() {
let mut v = vec![0; 32];
let block = &mut v[0..16];
do_stuff(block);
check_stuff(&v);
do_stuff(block);
}
fn do_stuff(_: &mut [i32]) {}
fn check_stuff(_: &[i32]) {}
The borrow checker complains:
error[E0502]: cannot borrow `v` as immutable because it is also borrowed as mutable
--> src/lib.rs:6:17
|
3 | let block = &mut v[0..16];
| - mutable borrow occurs here
...
6 | check_stuff(&v);
| ^^ immutable borrow occurs here
7 | do_stuff(block);
| ----- mutable borrow later used here
I'm aware of the kind of guarantee this is trying to provide. However, assuming I know what I'm doing, is there an idiomatic way to give a name to the slice without having to use do_stuff(&mut v[0..16]) or having to make copies every time?
I'm trying to nail down ownership rules. I want to:
start with a mutable reference to a slice
make some edits to its contents
reduce the slice reference to a reference of a sub-slice and repeat
Below is my attempt:
pub fn example() {
// Make a mutable slice
let mut v = [0, 1, 2, 3];
// Make a mutable reference to said slice
let mut v_ref = &mut v[..];
while v_ref.len() > 1 {
// Involves some edits -> need mut
v_ref.swap(0, v_ref.len() - 1);
// Try to reduce slice to sub-slice (some simplification here)
// Errors!
let (v_l, v_h) = v.split_at_mut(v.len() / 2);
v_ref = v_l;
}
}
However I'm getting the errors:
error[E0502]: cannot borrow `*v_ref` as immutable because it is also borrowed as mutable
--> src/lib.rs:11:23
|
11 | v_ref.swap(0, v_ref.len() - 1);
| ----- ^^^^^ - mutable borrow ends here
| | |
| | immutable borrow occurs here
| mutable borrow occurs here
error[E0499]: cannot borrow `v` as mutable more than once at a time
--> src/lib.rs:15:26
|
7 | let mut v_ref = &mut v[..];
| - first mutable borrow occurs here
...
15 | let (v_l, v_h) = v.split_at_mut(v.len() / 2);
| ^ second mutable borrow occurs here
...
18 | }
| - first borrow ends here
error[E0502]: cannot borrow `v` as immutable because it is also borrowed as mutable
--> src/lib.rs:15:41
|
7 | let mut v_ref = &mut v[..];
| - mutable borrow occurs here
...
15 | let (v_l, v_h) = v.split_at_mut(v.len() / 2);
| ^ immutable borrow occurs here
...
18 | }
| - mutable borrow ends here
I understand that you can't have multiple references to an object in the same scope as a single mutable reference.
There should be a safe way to reduce a slice's range, as you're only reducing the scope of a current mutable reference. What's the best way to do that?
Problem 1: Using the same variable mutably and immutably
As Peter Hall points out, the code attempts to reference the variable v while there's a concurrent mutable reference to it, even though you don't care about v_ref anymore. A MCVE:
pub fn example() {
let mut v = 0;
let mut v_ref = &mut v;
println!("{}", v)
}
See also:
What are non-lexical lifetimes?
Problem 2: Using the same variable mutably and immutably
Then the code attempts to overlap mutable and mutable borrows in a single function call. A MCVE:
pub fn example() {
let mut v = [0, 1, 2, 3];
v.split_at_mut(v.len());
}
See also:
Cannot borrow as immutable because it is also borrowed as mutable in function arguments
Problem 3: Using the same variable mutably and mutably
Then the code has overlapping mutable borrows of v in the loop.
See also:
Cannot obtain a mutable reference when iterating a recursive structure: cannot borrow as mutable more than once at a time
All together:
pub fn example() {
let mut v = [0, 1, 2, 3];
let mut v_ref = &mut v[..];
while v_ref.len() > 1 {
let len = v_ref.len();
v_ref.swap(0, len - 1);
let (v_l, _) = { v_ref }.split_at_mut(len / 2);
v_ref = v_l;
}
}
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?
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?
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?