dtolnay / async-trait

Type erasure for async trait methods
Apache License 2.0
1.81k stars 84 forks source link

Do not require Sync on unused shared reference arguments #232

Closed dtolnay closed 1 year ago

dtolnay commented 1 year ago
use async_trait::async_trait;

#[async_trait]
trait Generic<T> {
    async fn takes_ref(&self, thing: &T);
}

#[async_trait]
impl<T> Generic<T> for () {
    async fn takes_ref(&self, _: &T) {}
}

This currently fails because it expands to something like:

impl<T> Generic<T> for () {
    fn takes_ref<'life0, 'life1, 'async_trait>(
        &'life0 self,
        __arg1: &'life1 T,
    ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
    where
        'life0: 'async_trait,
        'life1: 'async_trait,
        Self: 'async_trait,
    {
        Box::pin(async move {
            let __self = self;
            let __arg1 = __arg1;
            let _: () = {};
        })
    }
}

in which the anonymous future type captures a local variable of type &T, implying a need for T: Sync in order to get a Send future.

error: future cannot be sent between threads safely
  --> src/main.rs:10:38
   |
10 |     async fn takes_ref(&self, _: &T) {}
   |                                      ^^ future created by async block is not `Send`
   |
note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync`
  --> src/main.rs:10:31
   |
8  | #[async_trait]
   | -------------- in this procedural macro expansion
9  | impl<T> Generic<T> for () {
10 |     async fn takes_ref(&self, _: &T) {}
   |                               ^ has type `&T` which is not `Send`, because `T` is not `Sync`
   = note: required for the cast from `[async block@src/main.rs:10:38: 10:40]` to the object type `dyn Future<Output = ()> + Send`
   = note: this error originates in the attribute macro `async_trait` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider restricting type parameter `T`
   |
9  | impl<T: std::marker::Sync> Generic<T> for () {
   |       +++++++++++++++++++

The following compiles:

#[async_trait]
impl<T: Sync> Generic<T> for () {
    async fn takes_ref(&self, _: &T) {}
}

However that T: Sync bound should be unnecessary as long as the &T argument is unused within the future.