Sending trait objects between threads in Rust

guff picture guff · Sep 3, 2014 · Viewed 14.4k times · Source

I'd like to send a trait object between threads, but can't figure out if it's possible. It seems like it might not be, as they apparently do not fulfill the Send trait.

The following code demonstrates what I'm trying to do:

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();
}

This fails with the following message:

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

Trying to send a plain, unboxed trait object results in a bunch of other errors, mostly complaining about not fulfilling Send + Sized.

I'm still fairly new to Rust, so I'm not sure if there's something I'm missing, but I get the impression that there isn't a way to convince the compiler to make the trait object Send.

If it isn't currently possible, is there any work currently under way that may allow for this in the future?

Answer

A.B. picture A.B. · Sep 3, 2014

It's possible. You can add a Send constraint to a trait object like this:

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();