I am a beginner.
error[E0507]: cannot move out of a shared reference. occurs within a method that returns a tuple of average and total values in two threads. What code is desired to solve this in a simple way?
pub fn calc_avg_and_sum_use_mutex(values: Vec<u64>) -> Result<(u64, u64)> {
let params = Arc::new(Mutex::<Vec<u64>>::new(values));
let mut handles: Vec<JoinHandle<u64>> = Vec::with_capacity(2);
let builder = Builder::new().name("avg".to_owned()).stack_size(1024 * 3);
let clone_param = Arc::clone(¶ms);
handles.push(builder.spawn(move || Self::calc_avg(clone_param))?);
let builder = Builder::new().name("sum".to_owned()).stack_size(1024 * 3);
let clone_param = Arc::clone(¶ms);
handles.push(builder.spawn(move || Self::calc_sum(clone_param))?);
let avg = handles.get(0).unwrap().join().unwrap();
let sum = handles.get(1).unwrap().join().unwrap();
Ok((avg, sum))
}
error[E0507]: cannot move out of a shared reference
43 | let avg = handles.get(0).unwrap().join().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------
| | |
| | value moved due to this method call
| move occurs because value has type `std::thread::JoinHandle<u64>`, which does not implement the `Copy` trait
Might be simplest to just keep the two handles in two separate variables here ^^
Otherwise.. assuming ownership of a Vec
's item is possible through various methods. To name just a few, you could use use Vec::remove
or ::pop
methods, or you could use a by-value iterator either via explicit .into_iter()
or implicitly in a for loop such as for handle in handles { … }
2 Likes
get
only gives you a reference to the items in a Vec
, so you can't call methods that require ownership like join
.
Since there's only two handles, you can just assign them to their own variables.
pub fn calc_avg_and_sum_use_mutex(values: Vec<u64>) -> Result<(u64, u64)> {
let params = Arc::new(Mutex::new(values));
let builder = Builder::new().name("avg".to_owned()).stack_size(1024 * 3);
let clone_param = Arc::clone(¶ms);
let handle_avg = builder.spawn(move || Self::calc_avg(clone_param))?;
let builder = Builder::new().name("sum".to_owned()).stack_size(1024 * 3);
let clone_param = Arc::clone(¶ms);
let handle_sum = builder.spawn(move || Self::calc_sum(clone_param))?;
let avg = handle_avg.join().unwrap();
let sum = handle_sum.join().unwrap();
Ok((avg, sum))
}
If you have to use a Vec
, like if you're making a bunch of threads in a loop, you can call pop
or into_iter
to obtain ownership of the items in the Vec
.
// The handles pop in reverse order, so sum is first.
let sum = handles.pop().unwrap().join().unwrap();
let avg = handles.pop().unwrap().join().unwrap();
let mut handles_iter = handles.into_iter();
let avg = handles_iter.next().unwrap().join().unwrap();
let sum = handles_iter.next().unwrap().join().unwrap();
However, your code right now isn't going to run in parallel. It'll run on separate threads, but it'll run sequentially. You can fix this by removing the Mutex
and updating the calc
functions to match.
You can also use thread::scope
to avoid needing the Arc
.
pub fn calc_avg_and_sum_use_mutex(values: &[u64]) -> Result<(u64, u64)> {
std::thread::scope(|scope| {
let builder = Builder::new().name("avg".to_owned()).stack_size(1024 * 3);
let handle_avg = builder.spawn_scoped(scope, move || Self::calc_avg(values))?;
let builder = Builder::new().name("sum".to_owned()).stack_size(1024 * 3);
let handle_sum = builder.spawn_scoped(scope, move || Self::calc_sum(values))?;
Ok((handle_avg.join().unwrap(), handle_sum.join().unwrap()))
})
}
2 Likes
The main problem is that the ownership of the value held in mut Vec can be changed by other methods.
Therefore, the solution of using two variables to avoid owning the JoinHandle in Vec is very simple and easy to understand.
Thanks for your reply.
I will try to find a solution to this problem.
I didn't know about the thread::scope solution, and now I clearly understand the need to use pop and next.
Thank you very much for your detailed explanation.
Thank you for sharing your great knowledge.
1 Like