Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

rust

I want to return a value from a function which is protected by a Mutex, but cannot understand how to do it properly. This code does not work:

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

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().expect("Unable to join thread");

    let guard = result_my.lock().unwrap();
    *guard
}

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

Playground

The compiler complains:

error[E0507]: cannot move out of borrowed content
  --> src/main.rs:16:5
   |
16 |     *guard
   |     ^^^^^^ cannot move out of borrowed content
like image 895
swizard Avatar asked Mar 20 '15 23:03

swizard


People also ask

How do you unlock mutex in Rust?

An RAII guard is returned to allow scoped unlock of the lock. When the guard goes out of scope, the mutex will be unlocked.

How does ARC work in Rust?

'Arc' stands for 'Atomically Reference Counted'. The type Arc<T> provides shared ownership of a value of type T , allocated in the heap. Invoking clone on Arc produces a new Arc instance, which points to the same allocation on the heap as the source Arc , while increasing a reference count.

How does mutex work rust?

Mutexes Allow Access to Data from One Thread at a Time. A mutex is an abbreviation for “mutual exclusion,” as in, it only allows one thread to access some data at any given time. To access the data in a mutex, a thread must first signal that it wants access by asking to acquire the mutex's lock.

What is arch length rust?

An integer the size of which is arch will be 32 bits on an x86 machine and 64 bits on an x64 machine.


2 Answers

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.

like image 68
Shepmaster Avatar answered Oct 03 '22 01:10

Shepmaster


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.

like image 35
swizard Avatar answered Oct 03 '22 01:10

swizard