0

Suppose we have a type that is not Send.

struct NotSend {
    field: std::rc::Rc<i32>
}

Then, following async function can still take NotSend as its parameter and compiles well:

async fn func(not_send: NotSend) -> i32 {
    0
}

But when I define the same function inside of the trait, then anything that implements it does not compile.

#[async_trait]
trait A {
    async fn func(not_send: NotSend) -> i32;
}

struct S {
    
}

#[async_trait]
impl A for S {
    async fn func(not_send: NotSend) -> i32 {
        0
    }
}

This fails with the following message:

error: future cannot be sent between threads safely
  --> src/main.rs:23:46
   |
23 |       async fn func( not_send: NotSend) -> i32 {
   |  ______________________________________________^
24 | |         0
25 | |     }
   | |_____^ future created by async block is not `Send`
   |
   = help: within `impl Future<Output = i32>`, the trait `Send` is not implemented for `Rc<i32>`
note: captured value is not `Send`
  --> src/main.rs:23:20
   |
23 |     async fn func( not_send: NotSend) -> i32 {
   |                    ^^^^^^^^ has type `NotSend` which is not `Send`
   = note: required for the cast to the object type `dyn Future<Output = i32> + Send`

What's so different from the naive function and the function in the trait? Why one does works but not the other? Playground Link

1 Answer 1

3

It's because async_trait expands to something like Pin<Box<dyn Future>>. If we want the resulting future to be Send, it needs to be Pin<Box<dyn Future + Send>>. But this forces it to be Send, i.e. will error on non-Send futures. The async_trait crate does not have a way to know whether the future is Send (because the implementation of the trait is different from its declaration, and we need to decide at declaration site whether the future will be Send or not), so it opts to use user-defined annotations.

By default, the generated type is Send since most futures needs to be Send. However, as explained in the documentation, you can use #[async_trait(?Send)] to opt this out:

#[async_trait(?Send)]
trait A {
    async fn func(not_send: NotSend) -> i32;
}

struct S {
    
}

#[async_trait(?Send)]
impl A for S {
    async fn func(not_send: NotSend) -> i32 {
        0
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Very minor correction, the return type generated by the macro is Pin<Box<dyn Future + Send>> (the Send bound is applied to the inner trait object, not the Pin).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.