2015-05-31 8 views
8

Ich möchte einen Verschluss über Kanäle senden:Ist es möglich, Schließungen über Kanäle zu senden?

use std::thread; 
use std::sync::mpsc; 

#[derive(Debug)] 
struct Test { 
    s1: String, 
    s2: String, 
} 

fn main() { 
    let t = Test { 
     s1: "Hello".to_string(), 
     s2: "Hello".to_string(), 
    }; 
    let (tx, rx) = mpsc::channel::<FnOnce(&mut Test)>(); 
    thread::spawn(move || { 
     let mut test = t; 
     let f = rx.recv().unwrap(); 
     f(&mut test); 
     println!("{:?}", test); 
    }); 
    tx.send(move |t: &mut Test| { 
     let s = "test".to_string(); 
     t.s1 = s; 
    }); 
} 

(playground)

ich ein paar Fehler erhalten:

error[E0277]: the trait bound `for<'r> std::ops::FnOnce(&'r mut Test): std::marker::Sized` is not satisfied 
    --> src/main.rs:15:20 
    | 
15 |  let (tx, rx) = mpsc::channel::<FnOnce(&mut Test)>(); 
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `for<'r> std::ops::FnOnce(&'r mut Test)` does not have a constant size known at compile-time 
    | 
    = help: the trait `std::marker::Sized` is not implemented for `for<'r> std::ops::FnOnce(&'r mut Test)` 
    = note: required by `std::sync::mpsc::channel` 

error[E0277]: the trait bound `for<'r> std::ops::FnOnce(&'r mut Test): std::marker::Sized` is not satisfied 
    --> src/main.rs:15:20 
    | 
15 |  let (tx, rx) = mpsc::channel::<FnOnce(&mut Test)>(); 
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `for<'r> std::ops::FnOnce(&'r mut Test)` does not have a constant size known at compile-time 
    | 
    = help: the trait `std::marker::Sized` is not implemented for `for<'r> std::ops::FnOnce(&'r mut Test)` 
    = note: required by `std::sync::mpsc::Sender` 

error[E0599]: no method named `recv` found for type `std::sync::mpsc::Receiver<for<'r> std::ops::FnOnce(&'r mut Test)>` in the current scope 
    --> src/main.rs:18:20 
    | 
18 |   let f = rx.recv().unwrap(); 
    |     ^^^^ 
    | 
    = note: the method `recv` exists but the following trait bounds were not satisfied: 
      `for<'r> std::ops::FnOnce(&'r mut Test) : std::marker::Sized` 

error[E0599]: no method named `send` found for type `std::sync::mpsc::Sender<for<'r> std::ops::FnOnce(&'r mut Test)>` in the current scope 
    --> src/main.rs:22:8 
    | 
22 |  tx.send(move |t: &mut Test| { 
    |  ^^^^ 
    | 
    = note: the method `send` exists but the following trait bounds were not satisfied: 
      `for<'r> std::ops::FnOnce(&'r mut Test) : std::marker::Sized` 

Es scheint, dass FnOnce nicht sendbar ist, aber ich don verstehe nicht warum.

Antwort

9

Ja. Es gibt ein paar Probleme mit Ihrem Code.

Zunächst ist FnOnce ein Merkmal, so dass Sie es nicht direkt verwenden können. Traits müssen entweder eine Beschränkung auf einen konkreten Typ oder eine Art Indirektion sein. Da Sie die Schließung an einen anderen Ort senden, wollen Sie etwas wie Box<FnOnce(...)>.

Zweitens können Sie nicht Box<FnOnce(...)> verwenden, da aufgrund Sicherheitsregeln zu widersprechen, kann man nicht wirklich Anruf ein FnOnce durch einen Umweg.

(Nebenbei, Sie auch nicht wollen, FnOnce<...> Syntax verwenden, die technisch nicht stabil ist, verwenden FnOnce(...) statt.)

Um dies zu lösen, können Sie entweder Schalter auf Fn oder FnMutoder Verwendung das noch nicht stabile FnBox Merkmal. Ich bin diesen Weg auf der Grundlage gegangen, dass er wahrscheinlich die von Ihnen gewünschte Semantik hat und wahrscheinlich in naher Zukunft stabilisiert wird. Wenn Sie sich damit nicht wohl fühlen, müssen Sie Ihren Verschluss entsprechend modifizieren.

Das folgende ist eine gemeinsame Anstrengung zwischen mir und Manishearth (die ich verpasst hatte den + Send Einschränkung darauf hingewiesen):

// NOTE: Requires a nightly compiler, as of Rust 1.0. 

#![feature(core)] 
use std::boxed::FnBox; 
use std::thread; 
use std::sync::mpsc; 

#[derive(Debug)] 
struct Test { 
    s1: String, 
    s2: String, 
} 

type ClosureType = Box<FnBox(&mut Test) + Send>; 

fn main() { 
    let t = Test { s1: "Hello".to_string(), s2: "Hello".to_string() }; 
    let (tx, rx) = mpsc::channel::<ClosureType>(); 

    thread::spawn(move || { 
     let mut test = t; 
     let f = rx.recv().unwrap(); 
     f.call_box((&mut test,)); 
     println!("{:?}", test); 
    }); 

    tx.send(Box::new(move |t: &mut Test| { 
     let s = "test".to_string(); 
     t.s1 = s; 
    })).unwrap(); 

    // To give the output time to show up: 
    thread::sleep_ms(100); 
} 
1

Die akzeptierte Antwort geht nicht ins Detail, aber Sie können senden Verschlüsse zu Fäden über Kanäle, auch auf stabilem, wenn Sie nicht verwenden FnOnce:

use std::thread; 
use std::sync::mpsc; 

struct RawFunc { 
    data: Box<Fn() + Send + 'static>, 
} 

impl RawFunc { 
    fn new<T>(data: T) -> RawFunc 
    where 
     T: Fn() + Send + 'static, 
    { 
     return RawFunc { 
      data: Box::new(data), 
     }; 
    } 

    fn invoke(self) { 
     (self.data)() 
    } 
} 

fn main() { 
    // Local 
    let x = RawFunc::new(move || { 
     println!("Hello world"); 
    }); 
    x.invoke(); 

    // Via channel 
    let (sx, rx) = mpsc::channel::<RawFunc>(); 
    sx.send(RawFunc::new(move || { 
     println!("Hello world 2"); 
    })).unwrap(); 
    let output = rx.recv().unwrap(); 
    output.invoke(); 

    // In a thread 
    let guard = thread::spawn(move || { 
     let output = rx.recv().unwrap(); 
     output.invoke(); 
    }); 

    sx.send(RawFunc::new(move || { 
     println!("Hello world 3!"); 
    })).unwrap(); 

    guard.join().unwrap(); 

    // Passing arbitrary data to a thread 
    let (sx, rx) = mpsc::channel::<RawFunc>(); 
    let guard = thread::spawn(move || { 
     let output = rx.recv().unwrap(); 
     output.invoke(); 
    }); 

    let foo = String::from("Hello World 4"); 
    sx.send(RawFunc::new(move || { 
     println!("Some moved data: {:?}", foo); 
    })).unwrap(); 

    guard.join().unwrap(); 
} 
Verwandte Themen