dtolnay / async-trait

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

Implement trait for async-trait instance method #167

Closed ebkalderon closed 2 years ago

ebkalderon commented 3 years ago

I'm running into the following apparent edge case with async-trait where a generic trait is being implemented for inherent async fn instance methods, but not on async-trait instance methods:

pub struct Service;

// This inherent `async fn` implements `Handler<S, P, R>` and is accepted by `Router::define_method`.
impl Service {
    async fn my_handler(&self) -> Result<(), ()> {
        Ok(())
    }
}

// But this async trait method is rejected if you replace the above code with the below.
//
// #[async_trait]
// trait MyHandlers {
//     async fn my_handler(&self) -> Result<(), ()>;
// }
//
// #[async_trait]
// impl MyHandlers for Service {
//     async fn my_handler(&self) -> Result<(), ()> {
//         Ok(())
//     }
// }

fn main() {
    let mut router = Router::new(Service);
    router.define_method("my_handler", Service::my_handler);
}

See the playground link for the minimal reproducible example.

It seems odd that the Handler trait isn't being implemented for methods generated by async-trait. I believe the included blanket impls should cover methods returning impl Future<Output = _>> + Send and Pin<Box<dyn Future<Output = _> + Send>> equally. Any ideas why this could be?

Might be related to #47, but I'm not sure. That issue is more about implementing an async-trait on top of any Fn() -> Fut, while this issue is getting a regular trait to apply equally to both inherent async fn and async-trait methods, but there could possibly be some overlap.

ebkalderon commented 3 years ago

After a bit more investigation, I believe this might indirectly be caused by https://github.com/rust-lang/rust/issues/64552, the same rustc bug that caused #141. Attempting to reduce this example any further runs into similar error messages quite a lot.

EDIT: My example seems strikingly similar to https://github.com/rust-lang/rust/issues/64552#issuecomment-666084589.

ebkalderon commented 3 years ago

Interestingly, this simplified example actually works as expected: Playground link

As soon as I try to use higher-kinded lifetimes and wrap the receiver in an Arc , then the lifetimes refuse to match up.

ebkalderon commented 3 years ago

A temporary workaround I've found is to wrap the trait method in a regular async fn like so:

async fn my_handler<T: MyHandlers>(receiver: &T) -> Result<(), ()> {
    receiver.my_handler(arg).await
}

router.define_method("my_handler", my_handler); // This works!
dtolnay commented 2 years ago

I'm glad you found a workaround. I am going to close this issue in favor of https://github.com/rust-lang/rust/issues/64552, since I believe this is a compiler bug.