Async fn in trait, current state?

To use asynchronous functions in traits, I'm currently using a mix of

and

  • a workaround with GATs and TAITs (see Playground) in other cases where I want no runtime overhead.

The latter works pretty well, except:

  • object safety (i think), and
  • a clunky syntax (involving async move and one extra level of indentation, see above Playground).

I have seen there has been an update on the Rust blog in Feburary 2022 regarding async fn in traits: Async Rust in 2022.

And there is also a feature gate #![feature(async_fn_in_traits)] for this, but apparently there is no implementation available on nightly Rust yet (see tracking issue).

So am I right that async-trait is still the way to go for now (or use GATs and TAITs if you want to avoid dynamic dispatch)?

2 Likes

Yes, I think no work has been done yet. Although the design for non object safe async traits is mostly done IIRC and it can be implemented (I'm not sure if the issue around auto traits was settled). Object safe async traits require more design work.

I don't mind object safety soooo much (though I stumbled upon it). What makes me wonder more if async traits will be Send or not (and where and how it is going to be defined). But that's apparently something yet being worked on, see comment in the PR and some of its responses:

ibraheemdev commented on Oct 27, 2021

I don't see anything mentioned about Sendness. Is requiring the future to be send a potential future extension?

FWIW, there are macros such as async-trait but which use the workarounds you mention (no Boxing!):

3 Likes

I knew about real-async-trait, but remember I ran into practical problems with it, plus the last release is quite a long time ago. But I didn't know about async_t, I'll check that, thanks!

1 Like

I'd be curious to hear about them (if you've already mentioned them in this forum feel free to link appropriately): even if I'm not the author of either crate, it doesn't mean I can't try to avoid, for my own crates, the API rough edges they may have had :slightly_smiling_face:

I don't remember my problems, but a quick search on the forum brought up this post of me where I wrote that real-async-trait didn't work for me.

P.S.: Like I said in that post, I vaguely remember that my function signatures involved quite complex lifetimes, or maybe it was impl/dyn Iterator in the arguments or return type or something like that, but I don't remember right now. Perhaps I find some of my related code later.

P.P.S.: Some of my code had/has signatures like this:

async fn foo<M>(
    &mut self,
    index: Index,
    ids: impl Iterator<Item = Id> + Send + 'async_trait,
    data_map: M,
) -> io::Result<()>
where
    M: FnMut(&Id) -> Data + Send + 'async_trait;

Here I use the special lifetime 'async_trait (used/defined by the async-trait crate) to indicate that the lifetime of the Iterator and the closure must be at least as long as the returned future. Also the methid is generic (over the closure M) (but the trait isn't).

But I don't remember if any of this caused a problem with real-async-trait. See also this issue that I filed for async-trait (which apparently was fixed in async-trait).

1 Like

Replacing async-trait with async_t didn't work well. It doesn't compile with rustc 1.63.0-nightly (ca122c7eb 2022-06-13), at least not on my machine (FreeBSD). Even in an empty project, it won't work:

   Compiling async_t_internal v0.6.0
error[E0599]: no variant or associated item named `__TestExhaustive` found for enum `syn::Type` in the current scope
   --> /home/jbe/.cargo/registry/src/github.com-1ecc6299db9ec823/async_t_internal-0.6.0/src/impl_trait/impl_trait.rs:143:15
    |
143 |         Type::__TestExhaustive(_) => abort!(ty.span(), "please report this bug"),
    |               ^^^^^^^^^^^^^^^^ variant or associated item not found in `syn::Type`

error[E0599]: no variant or associated item named `__TestExhaustive` found for enum `syn::Type` in the current scope
   --> /home/jbe/.cargo/registry/src/github.com-1ecc6299db9ec823/async_t_internal-0.6.0/src/impl_trait/implementation.rs:152:15
    |
152 |         Type::__TestExhaustive(_) => abort!(ty.span(), "please report this bug"),
    |               ^^^^^^^^^^^^^^^^ variant or associated item not found in `syn::Type`

For more information about this error, try `rustc --explain E0599`.
error: could not compile `async_t_internal` due to 2 previous errors
warning: build failed, waiting for other jobs to finish...

I filed an issue here.