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.
- Playground link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=04171e7fb65a9e158903978fd19ef8ac
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`
- Playground link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=23f61162b5be8384092888635d8daacc
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
versusimpl 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)]
.