在Rust中的线程之间发送特征对象

guf*_*uff 14 rust trait-objects

我想在任务之间发送一个特征对象,但无法弄清楚它是否可能.它似乎可能不是,因为它们显然不符合这一Send特性.

以下代码演示了我正在尝试做的事情:

use std::{
    sync::mpsc::{channel, Receiver, Sender},
    thread,
};

trait Bar {
    fn bar(&self);
}

struct Foo {
    foo: i32,
}

impl Bar for Foo {
    fn bar(&self) {
        println!("foo: {}", self.foo);
    }
}

fn main() {
    let foo = Box::new(Foo { foo: 1 }) as Box<dyn Bar>;

    let (tx, rx): (Sender<Box<dyn Bar>>, Receiver<Box<dyn Bar>>) = channel();

    thread::spawn(move || {
        tx.send(foo).unwrap();
    });

    let sent = rx.recv().unwrap();

    sent.bar();
}
Run Code Online (Sandbox Code Playgroud)

此操作失败,并显示以下消息:

error[E0277]: `dyn Bar` cannot be sent between threads safely
   --> src/main.rs:25:5
    |
25  |     thread::spawn(move || {
    |     ^^^^^^^^^^^^^ `dyn Bar` cannot be sent between threads safely
    |
    = help: the trait `std::marker::Send` is not implemented for `dyn Bar`
    = note: required because of the requirements on the impl of `std::marker::Send` for `std::ptr::Unique<dyn Bar>`
    = note: required because it appears within the type `std::boxed::Box<dyn Bar>`
    = note: required because it appears within the type `[closure@src/main.rs:25:19: 27:6 tx:std::sync::mpsc::Sender<std::boxed::Box<dyn Bar>>, foo:std::boxed::Box<dyn Bar>]`
Run Code Online (Sandbox Code Playgroud)

试图发送一个简单的,未装箱的特征对象会导致一堆其他错误,主要是抱怨没有实现Send + Sized.

我还是相当新的Rust,所以我不确定是否有我遗漏的东西,但我得到的印象是没有办法说服编译器制作特征对象Send.

如果目前无法实现,目前是否有任何工作可以在将来实现这一目标?

A.B*_*.B. 18

这是可能的.您可以Send向特征对象添加约束,如下所示:

let foo = Box::new(Foo { foo: 1 }) as Box<dyn Bar + Send>;

let (tx, rx): (Sender<Box<dyn Bar + Send>>, Receiver<Box<dyn Bar + Send>>) = channel();
Run Code Online (Sandbox Code Playgroud)

  • 我发现最简单的事情是在我的特征的所有实现上都要求“发送”,如下所示:“trait Bar : Send { ...”。显然,如果您可能有一些不需要“发送”的用途,您可能不想这样做。 (14认同)
  • 这可能对某人有用:就我而言,我还需要 `Sync` 特征,如 `Box&lt;dyn Bar + Send + Sync&gt;` 中。(我的用例是使用 [warp](https://github.com/seanmonstar/warp) 库的 Web 服务器)。 (3认同)
  • 哦,嗯,​​这很简单.我想我不太明白特征对象是如何实际处理的.所以这不是具有某种特质的特质,它是潜在的对象,对吗?因此,您必须让编译器知道该框正在保存特征,这也恰好满足了"发送".我想我现在明白了. (2认同)