Future is not Send, only when from async Trait

I have a trait Serializer:

trait Serializer {
    fn serialize(&self, data: Rc<()>) -> Result<(), Error>;
}

And some Foo which implements it. Notably, data is not Send.

The actual implementation requires an async context to execute, so I’ve split the creation of an async runtime away from the actual implementation, like so:

struct Foo {}

impl Foo {
    async fn async_serialize(&self, _data: Rc<()>) -> Result<(), Error> {
        unimplemented!();
    }
}

impl Serializer for Foo {
    fn serialize(&self, data: Rc<()>) -> Result<(), Error> {
        let runtime = Builder::new_current_thread().enable_all().build().unwrap();
        runtime.block_on(async move { self.async_serialize(data).await })
    }
}

This compiles and works how I would expect.

If I refactor the code such that the async Runtime creation is done by way of a trait:

#[async_trait]
trait AsyncSerializer {
    async fn async_serialize(&self, data: Rc<()>) -> Result<(), Error>;
}

#[async_trait]
impl AsyncSerializer for Foo {
    async fn async_serialize(&self, _data: Rc<()>) -> Result<(), Error> {
        unimplemented!();
    }
}

This does not compile, now complaining that Rc<()> isn’t Send:

error: future cannot be sent between threads safely
  --> src/main.rs:19:73
   |
19 |       async fn async_serialize(&self, _data: Rc<()>) -> Result<(), Error> {
   |  _________________________________________________________________________^
20 | |         unimplemented!();
21 | |     }
   | |_____^ future created by async block is not `Send`
   |
   = help: within `impl Future<Output = Result<(), anyhow::Error>>`, the trait `Send` is not implemented for `Rc<()>`
note: captured value is not `Send`

This error message makes sense to me, Rc is not Send, but:

  • Why was this not a problem before? The prior implementations (impl on Foo versus impl AsyncSerializer for Foo) look analogous to me.
  • Can I wrap data in some way to avoid this?

>Solution :

The way that #[async_trait] de-sugars your code requires your futures to be send, as explained here. To fix that change the attribute macro to be #[async_trait(?Send)].

Leave a Reply