Reputation: 3437
Consider the following code example, I have a vector of JoinHandlers
in which I need it iterate over to join back to the main thread, however, upon doing so I am getting the error error: cannot move out of borrowed content
.
let threads = Arc::new(Mutex::new(Vec::new()));
for _x in 0..100 {
let handle = thread::spawn(move || {
//do some work
}
threads.lock().unwrap().push((handle));
}
for t in threads.lock().unwrap().iter() {
t.join();
}
Upvotes: 8
Views: 7480
Reputation: 1266
As referenced in How to take ownership of T from Arc<Mutex<T>>? this is now possible to do without any trickery in Rust using Arc::try_unwrap
and Mutex::into_inner
let threads = Arc::new(Mutex::new(Vec::new()));
for _x in 0..100 {
let handle = thread::spawn(move || {
println!("{}", _x);
});
threads.lock().unwrap().push(handle);
}
let threads_unwrapped: Vec<JoinHandle<_>> = Arc::try_unwrap(threads).unwrap().into_inner().unwrap();
for t in threads_unwrapped.into_iter() {
t.join().unwrap();
}
Play around with it in this playground to verify.
Upvotes: 10
Reputation: 7
while the drain is a good solution, you can also do the following thing
// with a copy
let built_words: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![]));
let result: Vec<String> = built_words.lock().unwrap().clone();
// using drain
let mut locked_result = built_words.lock().unwrap();
let mut result: Vec<String> = vec![];
result.extend(locked_result.drain(..));
I would prefer to clone the data to get the original value. Not sure if it has any performance overhead.
Upvotes: -2
Reputation: 127711
Unfortunately, you can't do this directly. When Mutex
consumes the data structure you fed to it, you can't get it back by value again. You can only get &mut
reference to it, which won't allow moving out of it. So even into_iter()
won't work - it needs self
argument which it can't get from MutexGuard
.
There is a workaround, however. You can use Arc<Mutex<Option<Vec<_>>>>
instead of Arc<Mutex<Vec<_>>>
and then just take()
the value out of the mutex:
for t in threads.lock().unwrap().take().unwrap().into_iter() {
}
Then into_iter()
will work just fine as the value is moved into the calling thread.
Of course, you will need to construct the vector and push to it appropriately:
let threads = Arc::new(Mutex::new(Some(Vec::new())));
...
threads.lock().unwrap().as_mut().unwrap().push(handle);
However, the best way is to just drop the Arc<Mutex<..>>
layer altogether (of course, if this value is not used from other threads).
Upvotes: 13