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

Support async fn in trait methods (async/await follow-up) #2739

Open
bstrie opened this issue Aug 5, 2019 · 8 comments
Open

Support async fn in trait methods (async/await follow-up) #2739

bstrie opened this issue Aug 5, 2019 · 8 comments
Labels
A-async-await Proposals re. async / await A-traits Trait system related proposals & ideas AsyncAwait-Triaged T-lang Relevant to the language team, which will review and decide on the RFC.

Comments

@bstrie
Copy link
Contributor

bstrie commented Aug 5, 2019

As noted by the author of the async/await RFC during its discussion:

If the answer is just "this can't be used on trait methods", that removes a lot of the utility of this feature, especially for libraries.

This RFC does not propose enabling async trait methods. I agree that trait methods are an extremely valuable use case for async notation, and from my perspective it is the absolute highest priority extension after this initial MVP proposal (in contrast to things like async generators).

#2394 (comment)

Now that the async/await MVP is tentatively approaching stabilization, this is the next logical feature that users are going to start asking about. Discussion of how to support this feature is currently fragmented across various other async/await issues threads, so I have opened this issue in order to centralize discussion (and hopefully to have someone in-the-know summarize the current obstacles and plans).

cc @withoutboats

@steveklabnik
Copy link
Member

steveklabnik commented Aug 5, 2019 via email

@bstrie
Copy link
Contributor Author

bstrie commented Aug 5, 2019

After looking around I believe it is blocked on not only generic associated types but also existentials in traits, both of which have accepted RFCs (#1598 and #2071). The current metabug encompassing this work appears to be rust-lang/rust#63066 , although I'm unclear what subset of those features are required for async trait functions specifically (we may very well need a full RFC to determine that).

@Nemo157
Copy link
Member

Nemo157 commented Aug 6, 2019

The next step after existential associated types in traits might be to support -> impl Trait directly in traits via a desugaring to an associated type, something like that would seem necessary for async fn in traits as there is no way to link an existential associated type to an async fn in the implementation.

i.e. once GATs and "impl Trait in type aliases" are implemented it should be possible to do this:

trait Foo {
    type Bar<'a>: Future<Output = ()> + 'a;

    fn bar(&self) -> Self::Bar<'_>;
}

impl Foo for () {
    type Bar<'a> = impl Future<Output = ()> + 'a;

    fn bar(&self) -> Self::Bar<'_> {
        async move {
            println!("{:?}", self);
        }
    }
}

this could then be simplified to something like

trait Foo {
    fn bar(&self) -> impl Future<Output = ()> + '_;
}

impl Foo for () {
    fn bar(&self) -> impl Future<Output = ()> + '_ {
        async move {
            println!("{:?}", self);
        }
    }
}

and finally that should be fundamentally equivalent to

trait Foo {
    async fn bar(&self);
}

impl Foo for () {
    async fn bar(&self) {
        println!("{:?}", self);
    }
}

The biggest issues I see with the latter two sugars are how to deal with an unnameable associated type, e.g. first question I thought of is how do you use it as a trait object like you can with the first (type BoxDynFoo = Box<dyn Foo<Bar<'a> = Pin<Box<dyn Future<Output = ()> + 'a>>>>)?

@bstrie
Copy link
Contributor Author

bstrie commented Aug 6, 2019

The biggest issues I see with the latter two sugars are how to deal with an unnameable associated type

If the question is how to refer to a type that's been implicitly generated in such a way, perhaps that means having something like type_of!(Foo::bar), or something akin to associated types scoped to specific methods e.g. Foo::bar::Future (with the rationale of "if traits themselves can have input types and output types, why can methods only have input types?").

(Alternatively perhaps we could paper over it somehow with something like dyn impl Future, if people can tolerate that. :P )

@tustvold
Copy link

tustvold commented Aug 7, 2019

If you used the sugared notation for the trait would it be compatible with a non-async function returning a custom future type.

For example

trait Foo {
    async fn bar(&self);
}

impl Foo for () {
    fn bar(&self) -> impl Future<Output = ()> + '_ {
        MyFuture{...}
    }
}

@Nemo157
Copy link
Member

Nemo157 commented Aug 7, 2019

You can always write an async fn that simply awaits the custom future type

trait Foo {
    async fn bar(&self);
}

impl Foo for () {
    async fn bar(&self) {
        MyFuture{...}.await
    }
}

There is a reason to want a non-async fn in the implementation though (whether you're returning a custom future type or not), sometimes you want to do some minimal synchronous setup before returning

impl Foo for () {
    fn bar(&self) -> impl Future<Output = ()> + '_ {
        // sync part of function
        async {
            // async part of function
        }
    }
}

Because the trait definition already constrains the lifetimes involved this is mostly useful as an optimization, you may be able to reduce the size of the futures state by pre-calculating some data from the arguments.

@bstrie
Copy link
Contributor Author

bstrie commented Oct 28, 2019

Niko has a very good blog post on this topic: https://smallcultfollowing.com/babysteps/blog/2019/10/26/async-fn-in-traits-are-hard/

@jonas-schievink jonas-schievink added A-async-await Proposals re. async / await A-traits Trait system related proposals & ideas T-lang Relevant to the language team, which will review and decide on the RFC. labels Nov 13, 2019
@est31
Copy link
Member

est31 commented Sep 25, 2023

I think this is covered by rust-lang/rust#91611 , so can be closed?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-async-await Proposals re. async / await A-traits Trait system related proposals & ideas AsyncAwait-Triaged T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

No branches or pull requests

7 participants