Jacob Clark
Jacob Clark

Reputation: 3437

Cannot move data out of a Mutex

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

Answers (3)

Ethan
Ethan

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.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=9d5635e7f778bc744d1fb855b92db178

Upvotes: 10

sai umesh
sai umesh

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

Vladimir Matveev
Vladimir Matveev

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

Related Questions