rust - 是否可以通过 channel 发送闭包?

标签 rust

我想通过 channel 发送闭包:

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 似乎无法发送,但我不明白为什么。

最佳答案

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

首先,FnOnce是一个特征,所以你不能直接使用它。特征必须是对具体类型的约束,或者是某种间接的背后。由于您要将闭包发送到其他地方,因此您需要类似 Box<FnOnce(...)> 的东西.

其次,你不能使用Box<FnOnce(...)>因为,由于对象安全规则,您实际上不能调用 FnOnce通过间接寻址。

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

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

以下是我和 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);
}

关于rust - 是否可以通过 channel 发送闭包?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30557152/

相关文章:

rust - 如何防止值被 move ?

rust - 在 Rust 中重用变量

rust - 安装由 Cargo 构建的用于打包目的的二进制文件的推荐方法是什么?

rust - 有没有办法在匹配中使用自定义模式,例如正则表达式或函数?

rust - 找不到 `rayon` 的箱子

rust - 如何在 &[u8] 切片中找到子序列?

vector - 如何将迭代器元素的类型强制转换为新类型?

Rust 'for loop'(从 c++ 转换而来)

rust - 为什么这辈子活不过闭包?

rust - 为什么cloned()允许这个函数编译