Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Methods created are fn (&Struct) -> ..., rather than for<'_> fn (&Struct) -> ... #133

Closed
ids1024 opened this issue Oct 22, 2020 · 4 comments

Comments

@ids1024
Copy link

ids1024 commented Oct 22, 2020

I don't understand the sorcery of HRTBs and async well enough to know if there's a major difficulty with this, but it seems to be relevant when trying to accept the function as an argument.

Consider this:

use async_trait::async_trait;

struct Struct;

#[async_trait]
trait Trait {
    async fn g(&self);

    fn h(&self);
}

impl Struct {
    async fn f(&self) {}
}

#[async_trait]
impl Trait for Struct {
    async fn g(&self) {}

    fn h(&self) {}
}

fn main() {
    let _: () = Struct::f;
    let _: () = Struct::g;
    let _: () = Struct::h;
}

This tells us Struct::f has type for<'_> fn(&Struct) -> impl Future {Struct::f}. Struct::h has type for<'r> fn(&'r Struct) {<Struct as Trait>::h}.

But Struct::g is fn(&Struct) -> Pin<Box<dyn Future<Output = ()> + Send>> {<Struct as Trait>::g::<'_, '_>}.

If this can't be changed to have a for<'_> bound, I guess that should be documented as one of the crate's limitations...

@ids1024
Copy link
Author

ids1024 commented Oct 22, 2020

Looking at how the macro expands, it seems the for<'_> part is present with g1, but not g2:

    fn g1<'life0>(&'life0 self);

    fn g2<'life0, 'async_trait>(&'life0 self)
        where 'life0: 'async_trait;

I guess Rust introduces this for<'_> depending on the lifetimes involved in the function, but since this crate is partially bypassing Rust's lifetime inference with explicit lifetime bounds, it can't do that here.

@ids1024
Copy link
Author

ids1024 commented Oct 28, 2020

In particular, it seems it's the 'life0: 'async_trait bound that's responsible, though I don't know if there's a good alternative that will work...

Edit: This may be a limitation of the compiler.

@ids1024
Copy link
Author

ids1024 commented Oct 28, 2020

I guess the reason this works without async-trait is based on how lifetime elision works:

If there is exactly one lifetime used in the parameters (elided or not), that lifetime is assigned to all elided output lifetimes.

So with the async fn outside the trait, the return value's lifetime is inferred to be the same as the parameters.

I guess the "correct" solution would be for async-trait to apply all the lifetime elision rules the compiler does, but that isn't likely to be simple, I guess...

@ids1024
Copy link
Author

ids1024 commented Feb 1, 2021

It seems this is just an inconsistency in how Rustc prints the types. There was a real issue I was having, but as far as I can tell it's ultimately due to limits in Rustc.

@ids1024 ids1024 closed this as completed Feb 1, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant