How to impl "Send" for trait object

Hi everyone, I am a beginner of Rust. And now I encounter a problem on impl "Send" for trait object when using with tokio.

The code is the following:

use async_trait::async_trait;
use tokio::io::{AsyncRead, AsyncWrite};

#[async_trait]
pub trait Handler {
    async fn handle(&self) -> Result<(), std::io::Error>;
}

async fn serve( handler: std::sync::Arc<dyn Handler + Send>) {
    tokio::spawn(async move {
        let handler = handler;
        Ok(())
    });
}

fn main(){}

The error message is

error[E0277]: `dyn Handler + std::marker::Send` cannot be shared between threads safely
   --> src/main.rs:10:5
    |
10  |     tokio::spawn(async move {
    |     ^^^^^^^^^^^^ `dyn Handler + std::marker::Send` cannot be shared between threads safely
    | 
   ::: /Users/fbzhong/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/task/spawn.rs:127:21
    |
127 |         T: Future + Send + 'static,
    |                     ---- required by this bound in `tokio::task::spawn::spawn`
    |

Can anyone help me on this?
Thanks.

The solution offered by https://github.com/dtolnay/async-trait#dyn-traits is to add Send as a supertrait of the trait you're making into a trait object, rather than to add it as a bound. I think that'd look roughly like

...
#[async_trait]
pub trait Handler: Send {
    async fn handle(&self) -> Result<(), std::io::Error>;
}

async fn serve( handler: std::sync::Arc<dyn Handler>) {
...

Does this work for your use case / is this bound acceptable? If so, I think that's the best solution.

1 Like

Hi @daboross Thanks for replying. And I did try this method, and it have errors too:

error[E0277]: `dyn Handler` cannot be shared between threads safely
   --> src/main.rs:9:5
    |
9   |     tokio::spawn(async move {
    |     ^^^^^^^^^^^^ `dyn Handler` cannot be shared between threads safely
    | 
   ::: /Users/fbzhong/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/task/spawn.rs:127:21
    |
127 |         T: Future + Send + 'static,
    |                     ---- required by this bound in `tokio::task::spawn::spawn`
    |

Can you please take a look at this again? Really appreciate!

The code is

use async_trait::async_trait;

#[async_trait]
pub trait Handler: Send {
    async fn handle(&self) -> Result<(), std::io::Error>;
}

async fn serve( handler: std::sync::Arc<dyn Handler>) {
    tokio::spawn(async move {
        let handler = handler;
        Ok(())
    });
}

fn main(){}

I believe this is referring to the Sync trait, not Send.

Hi @CAD97,

I just tried "Sync" trait, same error :frowning:

tokio::spawn requires both Send and Sync, so if you only specify one (regardless of which one) you'll get a similar error. Requiring both should work:

pub trait Handler: Send + Sync {

There's another error in compilation, but that should also be easy to resolve. It can't tell the error type for the closure, so you need to specify it explicitly with Ok::<(), ()>(()) (using () for the error type) rather than Ok(()), or otherwise give it that information. With that change and Send + Sync, the example compiles for me.

2 Likes

Awesome! It works! Thank you so much!

Note that it is not true that tokio::spawn requires both; in fact it requires only Send. It's just that an Arc<T> is not Send unless T is both Send and Sync.

2 Likes

Oh, that explains the error [E0277]

127 |         T: Future + Send + 'static,
    |                     ---- required by this bound in `tokio::task::spawn::spawn`
    |

Great to know! Thank you very much!

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.