2015-05-31 14 views
8

Vorrei inviare una chiusura attraverso canali:È possibile inviare chiusure tramite canali?

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)

ho un mucchio di errori:

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` 

Sembra che FnOnce non è che posso mandare, ma io don capisco perché

risposta

9

Sì. Ci sono alcuni problemi con il tuo codice.

Prima di tutto, FnOnce è un tratto, quindi non è possibile utilizzarlo direttamente. I tratti devono essere o un vincolo su un tipo concreto, o dietro un riferimento indiretto di qualche tipo. Dal momento che stai inviando la chiusura da qualche altra parte, vuoi qualcosa come Box<FnOnce(...)>.

In secondo luogo, non è possibile utilizzare Box<FnOnce(...)> perché, a causa di opporsi norme di sicurezza, in realtà non chiamata un FnOnce attraverso un riferimento indiretto è possibile.

(Per inciso, anche voi non volete usare FnOnce<...> sintassi, che è tecnicamente instabile; utilizzare FnOnce(...) invece.)

per risolvere questo, è possibile passare a Fn o FnMuto uso il tratto non ancora stabile FnBox. Ho seguito questa strada sulla base del fatto che probabilmente ha la semantica desiderata e probabilmente sarà stabilizzata nel prossimo futuro. Se ti senti a disagio, dovrai modificare la chiusura in modo appropriato.

Quello che segue è uno sforzo congiunto tra me e Manishearth (che ha sottolineato che avevo perso il vincolo + Send):

// 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

La risposta accettata non va nel dettaglio, ma si può send chiusure a thread tramite canali, anche su stable, se non si utilizza 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(); 
} 
Problemi correlati