I'm running into a wall with the borrow checker, I think because it can't tell things are no longer used by the worker after it is joined by the main thread. I've reduced my problem to the simplest possible example of the same pattern that I could. I'm trying to avoid unnecessary allocations and copies, especially of b.
fn sequential_fn() -> Vec<i32>
{
let mut a = vec![1,2,3,4]; //two mutable arrays
let mut b = vec![0;4];
for repetitions in 0..100
{
for i in 0..4
{
b[i] ^= a[i]; //example heavy operation - a isnt modified but b is
}
b.sort(); //heavy operation over, b is sorted
for e in a.iter_mut() //a is modified by main thread before repeating process
{ *e += 1;}
}
return b; //return b
}
The sequential version above compiles and works fine. This is how I've attempted to parallelize the heavy operation to split up the workload:
fn parallel_fn() -> Vec<i32>
{
let mut a = vec![1,2,3,4];
let mut b = vec![0;4];
let (mut b1 , mut b2) = b.split_at_mut(2); //split b into slices for each worker (err1)
for repetitions in 0..100
{
//only 1 worker for example purposes
let worker = std::thread::spawn(|| //(err2)
{
for i in 2..4
{
b2[i-2] ^= a[i] //mutably borrow b2, immutably borrow a
}
});
for i in 0..2
{
b1[i] ^= a[i]; // mutably borrow b1, immutably borrow a
}
worker.join(); //workers finish
b.sort(); //borrow b as mutable in main thread only (err3)
for e in a.iter_mut() //borrow a as mutable in main thread only (err4)
{ *e += 1;}
}
return b;
}
The errors I'm getting are:
error[E0597]: `b` does not live long enough
--> src/lib.rs:5:29
|
5 | let (mut b1 , mut b2) = b.split_at_mut(2); //split b into slices for each worker (err1)
| ^^^^^^^^^^^^^^^^^
| |
| borrowed value does not live long enough
| argument requires that `b` is borrowed for `'static`
...
27 | }
| - `b` dropped here while still borrowed
error[E0499]: cannot borrow `*b2` as mutable more than once at a time
--> src/lib.rs:10:41
|
10 | let worker = std::thread::spawn(|| //(err2)
| - ^^ `*b2` was mutably borrowed here in the previous iteration of the loop
| ______________________|
| |
11 | | {
12 | | for i in 2..4
13 | | {
14 | | b2[i-2] ^= a[i] //mutably borrow b2, immutably borrow a
| | -- borrows occur due to use of `*b2` in closure
15 | | }
16 | | });
| |__________- argument requires that `*b2` is borrowed for `'static`
error[E0373]: closure may outlive the current function, but it borrows `a`, which is owned by the current function
--> src/lib.rs:10:41
|
10 | let worker = std::thread::spawn(|| //(err2)
| ^^ may outlive borrowed value `a`
...
14 | b2[i-2] ^= a[i] //mutably borrow b2, immutably borrow a
| - `a` is borrowed here
|
note: function requires argument type to outlive `'static`
--> src/lib.rs:10:22
|
10 | let worker = std::thread::spawn(|| //(err2)
| ______________________^
11 | | {
12 | | for i in 2..4
13 | | {
14 | | b2[i-2] ^= a[i] //mutably borrow b2, immutably borrow a
15 | | }
16 | | });
| |__________^
help: to force the closure to take ownership of `a` (and any other referenced variables), use the `move` keyword
|
10 | let worker = std::thread::spawn(move || //(err2)
| ++++
error[E0499]: cannot borrow `b` as mutable more than once at a time
--> src/lib.rs:22:9
|
5 | let (mut b1 , mut b2) = b.split_at_mut(2); //split b into slices for each worker (err1)
| ----------------- first mutable borrow occurs here
...
19 | b1[i] ^= a[i]; // mutably borrow b1, immutably borrow a
| ----- first borrow later used here
...
22 | b.sort(); //borrow b as mutable in main thread only (err3)
| ^^^^^^^^ second mutable borrow occurs here
error[E0502]: cannot borrow `a` as mutable because it is also borrowed as immutable
--> src/lib.rs:23:18
|
10 | let worker = std::thread::spawn(|| //(err2)
| - -- immutable borrow occurs here
| ______________________|
| |
11 | | {
12 | | for i in 2..4
13 | | {
14 | | b2[i-2] ^= a[i] //mutably borrow b2, immutably borrow a
| | - first borrow occurs due to use of `a` in closure
15 | | }
16 | | });
| |__________- argument requires that `a` is borrowed for `'static`
...
23 | for e in a.iter_mut() //borrow a as mutable in main thread only (err4)
| ^^^^^^^^^^^^ mutable borrow occurs here
error[E0505]: cannot move out of `b` because it is borrowed
--> src/lib.rs:26:12
|
5 | let (mut b1 , mut b2) = b.split_at_mut(2); //split b into slices for each worker (err1)
| -----------------
| |
| borrow of `b` occurs here
| argument requires that `b` is borrowed for `'static`
...
26 | return b;
| ^ move out of `b` occurs here
Playground
You've made a bunch of assumptions in your code which rust borrow checker can't ensure in compile type:
When parallel_fn finishes, a and b are removed. You are calling .join() which ensures the spawned thread should finish by that time, but it is a runtime check, rust can't check it in compile time.
That is why you can't pass reference to spawn, so you need arc
You are calling .join() which ensures no read operation over a happens when you modify it, but compiler can't check this.
You mutate parts of the same vector in different threads, which is not safe unless you take extra care, so you need a little bit of unsafe code. Alternative would be split the vector into 2 vectors (not slices) and join them, but it is extra copying.
I've ended up with something like this:
use std::sync::{Mutex, Arc, RwLock};
#[derive(Debug)]
struct VectorHolder {
vec: Vec<i32>,
slice1_ptr: Mutex<*mut [i32]>,
slice2_ptr: Mutex<*mut [i32]>,
}
impl VectorHolder {
pub fn new(mut vec: Vec<i32>) -> Self {
let (slice1 , slice2) = vec.split_at_mut(2);
let slice1_ptr = slice1 as *mut _;
let slice2_ptr = slice2 as *mut _;
Self {
vec,
slice1_ptr: Mutex::new(slice1_ptr),
slice2_ptr: Mutex::new(slice2_ptr),
}
}
/// Below operations are "safe" as no one else can access parts of Vec until
/// VectorHolder is consumed.
/// It is also safe we can't call op1 from 2 threads due to the mutex.
/// Mutex could optionally be removed if you are sure you never call op1 and op2 concurrently.
/// In this case we are sure about that as we have .join
pub fn op1(&self, a: &[i32]) {
let mut guard = self.slice2_ptr.lock().unwrap();
let b2 = unsafe { &mut **guard };
for i in 2..4 {
b2[i-2] ^= a[i] //mutably borrow b2, immutably borrow a
}
}
pub fn op2(&self, a: &[i32]) {
let mut guard = self.slice1_ptr.lock().unwrap();
let b1 = unsafe { &mut **guard };
for i in 0..2 {
b1[i] ^= a[i]; // mutably borrow b1, immutably borrow a
}
}
pub fn consume(self) -> Vec<i32> {
self.vec
}
}
unsafe impl Send for VectorHolder { }
unsafe impl Sync for VectorHolder { }
pub fn operations_on_b(b: Vec<i32>, a: Arc<RwLock<Vec<i32>>>) -> Vec<i32> {
let holder = Arc::new(VectorHolder::new(b));
//only 1 worker for example purposes
let holder_clone = holder.clone();
let a_clone = a.clone();
let worker = std::thread::spawn(move || {
holder_clone.op1(a_clone.read().unwrap().as_slice());
});
holder.op2(a.read().unwrap().as_slice());
worker.join().unwrap(); //workers finish
let mut modified_b = Arc::try_unwrap(holder).unwrap().consume();
modified_b.sort();
modified_b
}
fn parallel_fn() -> Vec<i32>
{
let a = Arc::new(RwLock::new(vec![1,2,3,4]));
let mut b = vec![0;4];
for _repetitions in 0..100
{
b = operations_on_b(b, a.clone());
for e in a.write().unwrap().iter_mut() //borrow a as mutable in main thread only (err4)
{ *e += 1;}
}
return b;
}
fn main() {
println!("{:?}", parallel_fn());
}
Related
I have some code like this (playground). Why does the compiler think it's a problem? How can i solve this problem without adding new variable that storing argument as the compiler tells?
Code:
struct A {}
impl A {
fn foo(&mut self, i: i32) -> i32 {
i
}
fn bar(&mut self) -> i32 {
1
}
}
fn main() {
let mut b = A{};
b.foo(b.bar());
}
Error:
error[E0499]: cannot borrow `b` as mutable more than once at a time
--> src/main.rs:15:11
|
15 | b.foo(b.bar());
| ------^^^^^^^-
| | | |
| | | second mutable borrow occurs here
| | first borrow later used by call
| first mutable borrow occurs here
|
help: try adding a local storing this argument...
--> src/main.rs:15:11
|
15 | b.foo(b.bar());
| ^^^^^^^
help: ...and then using that local as the argument to this call
--> src/main.rs:15:5
|
15 | b.foo(b.bar());
| ^^^^^^^^^^^^^^
Taking apart that your methods do not need to be &mut and that is just for the example.
How can i solve this problem without adding new variable that storing argument as the compiler tells?
At the moment, you can't. The way is to move the inner result, so the borrow is unlocked on the next call:
fn main() {
let mut b = A{};
let i = b.bar();
b.foo(i);
}
Playground
This question already has answers here:
Returning a reference from a HashMap or Vec causes a borrow to last beyond the scope it's in?
(1 answer)
When is it necessary to circumvent Rust's borrow checker?
(1 answer)
Closed 1 year ago.
use std::collections::HashMap;
#[derive(Clone, Hash, Eq, PartialEq)]
struct HeavyCloneKey;
struct Map<V> {
map : HashMap<HeavyCloneKey, V>
}
impl<V> Map<V> {
pub fn get_mut(&mut self, key: &HeavyCloneKey) -> Option<&mut V> {
if let Some(v) = self.map.get_mut(key) {
return Some(v);
}
// some logic to decide if create a new value;
if self.map.len() > 64 {
None
} else {
let v = create_v_with_long_code();
let v = self.map.entry(key.clone()).or_insert_with(|| v);
Some(v)
}
}
}
fn create_v_with_long_code<V>() -> V {
unimplemented!()
}
fn main() {
}
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=19c45ebfb1a39271ff5bd2443399596a
I can't understand why this can't work:
error[E0502]: cannot borrow `self.map` as immutable because it is also borrowed as mutable
--> src/main.rs:18:12
|
12 | pub fn get_mut(&mut self, key: &HeavyCloneKey) -> Option<&mut V> {
| - let's call the lifetime of this reference `'1`
13 | if let Some(v) = self.map.get_mut(key) {
| -------- mutable borrow occurs here
14 | return Some(v);
| ------- returning this value requires that `self.map` is borrowed for `'1`
...
18 | if self.map.len() > 64 {
| ^^^^^^^^ immutable borrow occurs here
These two borrows won't appear at the same time, what's the problem? How can I do this?
Although the code can be compiled by using contains_key before get_mut, it pays more overhead;
It helps a little to desugar the lifetimes by providing your own annotations on the references. It doesn't really matter here though as lifetimes aren't actually the issue.
pub fn get_mut<'s, 'k>(&'s mut self, key: &'k HeavyCloneKey) -> Option<&'s mut V> {
This gets us a little better error message:
error[E0502]: cannot borrow `self.map` as immutable because it is also borrowed as mutable
--> src/main.rs:18:12
|
12 | pub fn get_mut<'s, 'k>(&'s mut self, key: &'k HeavyCloneKey) -> Option<&'s mut V> {
| -- lifetime `'s` defined here
13 | if let Some(v) = self.map.get_mut(key) {
| -------- mutable borrow occurs here
14 | return Some(v);
| ------- returning this value requires that `self.map` is borrowed for `'s`
...
18 | if self.map.len() > 64 {
| ^^^^^^^^ immutable borrow occurs here
error[E0499]: cannot borrow `self.map` as mutable more than once at a time
--> src/main.rs:22:21
|
12 | pub fn get_mut<'s, 'k>(&'s mut self, key: &'k HeavyCloneKey) -> Option<&'s mut V> {
| -- lifetime `'s` defined here
13 | if let Some(v) = self.map.get_mut(key) {
| -------- first mutable borrow occurs here
14 | return Some(v);
| ------- returning this value requires that `self.map` is borrowed for `'s`
...
22 | let v = self.map.entry(key.clone()).or_insert_with(|| v);
| ^^^^^^^^ second mutable borrow occurs here
We have two errors here. Both stem from the rust compiler not being able to tell that we don't use the mutable borrow from line 13 later in the function.
The first says "you grabbed a mutable reference to self here so we can't let you have an immutable reference there". The second says "you grabbed a mutable reference to self here so we can't let you have a second mutable reference there".
How do we fix this? By making it clearer to the compiler that we'll never hold that first mutable reference. We can do this by manually checking if that key exists first. Change this
if let Some(v) = self.map.get_mut(key) {
return Some(v);
}
to this
if self.map.contains_key(key) {
return self.map.get_mut(key);
}
and it will compile.
Here is my code, and the compiler error beneath.
fn main() {
let mut s = String::new();
let mut push_if = |b, some_str| {
if b {
s.push_str(some_str);
}
};
push_if(s.is_empty(), "Foo");
println!("{}", s);
}
error[E0502]: cannot borrow `s` as immutable because it is also borrowed as mutable
--> src/main.rs:8:13
|
3 | let mut push_if = |b, some_str| {
| ------------- mutable borrow occurs here
4 | if b {
5 | s.push_str(some_str);
| - first borrow occurs due to use of `s` in closure
...
8 | push_if(s.is_empty(), "Foo");
| ------- ^ immutable borrow occurs here
| |
| mutable borrow later used by call
Why is the compiler complaining about s.is_empty() being an immutable borrow?
I'm just trying to return a bool, which doesn't seem like I'm borrowing anything. What changes do I need to make to compile the program successfully?
You can try this:
fn main() {
let mut s = String::new();
let mut push_if = |b, some_str, string: &mut String| {
if b {
string.push_str(some_str);
}
};
push_if(s.is_empty(), "Foo", &mut s);
println!("{}", s);
}
I'm having trouble understanding why I can't use v a second time, when it seems that the first mutable borrow has gone out of scope:
fn get_or_insert(v: &mut Vec<Option<i32>>, index: usize, default: i32) -> &mut i32 {
if let Some(entry) = v.get_mut(index) { // <-- first borrow here
if let Some(value) = entry.as_mut() {
return value;
}
}
// error[E0502]: cannot borrow `*v` as immutable because it is also borrowed as mutable
while v.len() <= index { // <-- compiler error here
v.push(None);
}
// error[E0499]: cannot borrow `*v` as mutable more than once at a time
let entry = v.get_mut(index).unwrap(); // <-- compiler error here
*entry = Some(default);
entry.as_mut().unwrap()
}
playground link
Do I have my variable scopes wrong, or is the borrow checker protecting me from something I'm not seeing?
Edit: the error message with NLL enabled is pretty good:
error[E0502]: cannot borrow `*v` as immutable because it is also borrowed as mutable
--> src/main.rs:10:11
|
3 | fn get_or_insert(v: &mut Vec<Option<i32>>, index: usize, default: i32) -> &mut i32 {
| - let's call the lifetime of this reference `'1`
4 | if let Some(entry) = v.get_mut(index) {
| - mutable borrow occurs here
5 | if let Some(value) = entry.as_mut() {
6 | return value;
| ----- returning this value requires that `*v` is borrowed for `'1`
...
10 | while v.len() <= index {
| ^ immutable borrow occurs here
The key point is that, even with NLL, the lifetime of the return value spans the whole function. The fact that the function returns early on line 4 is not taken into account when deciding whether the v reference is accessible in the code lower down.
The fix suggested by #Stargateur is to grow the vector if needed before accessing an element:
fn get_or_insert(v: &mut Vec<Option<i32>>, index: usize, value: i32) -> &mut i32 {
if v.len() < index {
v.resize(index + 1, None);
}
v[index].get_or_insert(value)
}
playground link
Here's where I used the technique in the final code.
use std::rc::Rc;
fn f1(cb: Box<Fn(i32) -> i32>) {
let res = cb(15);
println!("res {}", res);
}
fn main() {
let mut v2 = Rc::new(5_i32);
// 1
// f1(Box::new(move |x: i32| *v2 + x));
// 2
f1(Box::new(move |x: i32| {
let tmp = *v2;
*Rc::get_mut(&mut v2).unwrap() = tmp + 1;
x + *v2
}));
}
The code referenced as "1", if uncommented, compiles and runs just fine, but the code referenced as "2" does not compile, failing with the message:
error[E0596]: cannot borrow `v2` as mutable, as it is a captured variable in a `Fn` closure
How can I fix this, if I want keep the code structure as it is?
In my real code, I want connect two traits. One of them will call a callback on event, and the other has a function to handle the callback:
trait Foo {
fn add_callback(&mut self, cb: Box<Fn(i32)>);
}
trait Boo {
fn on_new_data(&mut self, data: i32);
}
I want to create a trait object with Boo, wrap it with Rc, and pass it to Foo::add_callback in the form of |x:i32| Rc::get_mut(&mut boo).unwrap().on_new_data(x)
The entire error message is mostly helpful:
error[E0596]: cannot borrow `v2` as mutable, as it is a captured variable in a `Fn` closure
--> src/main.rs:17:19
|
17 | *Rc::get_mut(&mut v2).unwrap() = tmp + 1;
| ^^^^^^^ cannot borrow as mutable
|
help: consider changing this to accept closures that implement `FnMut`
--> src/main.rs:15:17
|
15 | f1(Box::new(move |x: i32| {
| _________________^
16 | | let tmp = *v2;
17 | | *Rc::get_mut(&mut v2).unwrap() = tmp + 1;
18 | | x + *v2
19 | | }));
| |_____^
Changing f1 to accept a FnMut and making the variable mutable allows the code to compile:
fn f1(mut cb: Box<FnMut(i32) -> i32>) {
This is needed in order to mutate the captured variable v2, required by the &mut v2 argument to Rc::get_mut.