在 Rust 中在线程之间发送 trait 对象

70

我想在线程之间发送特质对象,但无法确定是否可能。似乎不行,因为它们显然不满足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();
}

这个操作失败了,并显示以下信息:

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>]`

试图发送一个未经包装的普通特质对象将导致大量其他错误,主要是关于未满足Send + Sized的投诉。

我对Rust还不是很熟悉,所以我不确定是否有什么我错过的东西,但我有这样的印象,即没有办法说服编译器使trait object成为Send

如果当前无法实现,是否有任何正在进行的工作可能会在未来允许这样做?

1个回答

76

是可能的。您可以向特质对象添加一个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();

4
哦,啊,那很简单。我猜我之前没有完全理解trait对象的处理方式。所以不是trait拥有一种类型,而是底层对象拥有,对吧?因此,你只需要让编译器知道该盒子中装的东西具有这个trait,并且也满足Send即可。我现在明白了。 - guff
8
这可能对某些人有用:在我的情况下,我还需要 Sync trait,就像这样 Box<dyn Bar + Send + Sync>(我的用例是为一个使用warp库的 Web 服务器)。 - Meet Sinojia
13
我发现最简单的做法是要求在我特质的所有实现中都包括 Send,就像这样:trait Bar : Send { ...。显然,如果可能会有一些不需要 Send 的使用情况,那么您可能不想这样做。 - Timmmm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接