2015-03-20 4 views
9

Ich möchte einen Wert von einer Funktion, die durch eine Mutex geschützt ist, aber nicht verstehen, wie man es richtig macht. Dieser Code funktioniert nicht:So übernehmen Sie den Besitz von T von Arc <Mutex<T>>?

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

Der Compiler beschwert sich:

error[E0507]: cannot move out of borrowed content 
    --> src/main.rs:16:5 
    | 
16 |  *guard 
    |  ^^^^^^ cannot move out of borrowed content 
+0

Was würden Sie wie das Verhalten von 'result_his.lock()' sein, nachdem der Wert von 'result_my' bewegen? – Shepmaster

+0

Idealerweise würde ich gerne etwas akzeptieren, das 'Mutex ' wertmäßig akzeptiert und' T' ergibt, bisher brauche ich diesen Mutex nicht mehr. Aber ok, ich verstehe jetzt, das könnte eine zu komplexe Lösung sein. Soll ich jetzt die Antwort von @SBSTP akzeptieren oder ist es besser, meine eigene Antwort zu geben und sie zu akzeptieren? – swizard

Antwort

6

Die beste Lösung, die ich so weit fand das Ergebnis in eine Option wickeln und dann herausnehmen:

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() 
} 

Es scheint besser als die mem::replace solution proposed by @SBSTP weil es keine Notwendigkeit gibt, eine leere T zum Auslagern zu konstruieren, und es verhindert mehrere Extraktionen.

2

Sie mem::replace Besitz eines wandelbaren Bezug auf Übertragung von Daten verwenden können, indem sie es mit einem neuen Wert zu ersetzen. (Der alte Wert zurückgegeben und bewegt)

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()); 
} 
+1

Danke für eine Idee! Aber in diesem Fall scheint es, dass meine 'Option' Lösung noch besser ist, weil ich kein nutzbares leeres' T' zweites Mal für 'mem :: replace' arg konstruieren muss (es könnte signifikant sein, wenn die Konstruktion nicht billig ist) . – swizard

+0

Nun, ich weiß nicht, ob Sie "None" als Konstruktion betrachten, aber 'take' verwendet' mem :: replace' und ersetzt den Wert durch 'None'. http://doc.rust-lang.org/src/core/option.rs.html#702 – SBSTP

+0

Ich meine, dass für mein Beispiel 'T' vom Typ' Ergebnis <(), String> 'ist, also ist es kein Problem,' OK (()) '' 'mem :: replace" Parameter, aber wenn es so etwas wie 'fs :: File' wäre, könnte es sich um eine nicht-triviale Aufgabe handeln. – swizard

8

In Rust 1.15 können Sie Arc::try_unwrap und Mutex::into_inner verwenden:

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()); 
} 
Verwandte Themen