How to take ownership of T from Arc<Mutex<T>>?

In Rust 1.15, you can use Arc::try_unwrap and Mutex::into_inner:

use std::sync::{Arc, Mutex};

fn func() -> Result<(), String> {
    let result_my = Arc::new(Mutex::new(Ok(())));
    let result_thread = result_my.clone();

    let t = std::thread::spawn(move || {
        let mut result = result_thread.lock().unwrap();
        *result = Err("something failed".to_string());
    });

    t.join().expect("Unable to join threads");

    let lock = Arc::try_unwrap(result_my).expect("Lock still has multiple owners");
    lock.into_inner().expect("Mutex cannot be locked")
}

fn main() {
    println!("func() -> {:?}", func());
}

RwLock::into_inner also exists since Rust 1.6.


The best solution I found so far is to wrap the result into an Option and then take it out:

fn func() -> Result<(), String> {
    let result_my = Arc::new(Mutex::new(Some(Ok(()))));
    let result_his = result_my.clone();

    let t = std::thread::spawn(move || {
        let mut result = result_his.lock().unwrap();
        *result = Some(Err("something failed".to_string()));
    });

    t.join().expect("Unable to join thread");

    let mut guard = result_my.lock().unwrap();
    guard.take().unwrap()
}

It seems better than the mem::replace solution proposed by @SBSTP because there is no need to construct an empty T for swapping, and it prevents multiple extractions.


You can use mem::replace to transfer ownership of a mutable reference by replacing it with a new value. (the old value is returned and moved)

use std::sync::{Arc, Mutex};
use std::mem;

fn func() -> Result<(), String> {
    let result_my = Arc::new(Mutex::new(Ok(())));
    let result_his = result_my.clone();

    let t = std::thread::spawn(move || {
        let mut result = result_his.lock().unwrap();
        *result = Err("something failed".to_string());
    });

    t.join();

    let mut guard = result_my.lock().unwrap();
    mem::replace(&mut guard, Ok(()))
}

fn main() {
    println!("func() -> {:?}", func());
}

Tags:

Rust