【问题标题】:Is it possible to send closures via channels?是否可以通过渠道发送闭包?
【发布时间】:2015-05-31 11:59:52
【问题描述】:

我想通过渠道发送闭包:

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)

我收到一堆错误:

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`

FnOnce 似乎无法发送,但我不明白为什么。

【问题讨论】:

    标签: rust


    【解决方案1】:

    是的。您的代码存在一些问题。

    首先FnOnce是一个trait,所以不能直接使用。特征必须要么是对具体类型的约束,要么是某种间接类型的约束。由于您要将闭包发送到其他地方,因此您需要 Box&lt;FnOnce(...)&gt; 之类的东西。

    其次,您不能使用Box&lt;FnOnce(...)&gt;,因为由于对象安全规则,您实际上不能通过间接调用FnOnce

    (顺便说一句,您也不想使用FnOnce&lt;...&gt; 语法,这在技术上是不稳定的;请改用FnOnce(...)。)

    要解决这个问题,您可以切换到FnFnMut 使用尚未稳定的FnBox trait。我走这条路是因为它可能具有您想要的语义,并且可能在不久的将来稳定下来。如果您对此感到不舒服,则需要适当地修改您的闭包。

    以下是我和 Manishearth 的共同努力(他指出我错过了+ 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);
    }
    

    【讨论】:

      【解决方案2】:

      接受的答案没有详细说明,但如果您不使用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();
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-11-08
        • 1970-01-01
        • 2012-10-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-09-09
        • 1970-01-01
        相关资源
        最近更新 更多