this post was submitted on 22 Jan 2026
20 points (100.0% liked)

Rust

7692 readers
11 users here now

Welcome to the Rust community! This is a place to discuss about the Rust programming language.

Wormhole

!performance@programming.dev

Credits

  • The icon is a modified version of the official rust logo (changing the colors to a gradient and black background)

founded 2 years ago
MODERATORS
 

I just ran into the wonderful error message

the trait is not dyn compatible because method publish_video is async

and boy, what a rabbit hole. I found out about async_trait which resolves this by turning async methods into fn method() -> Pin<Box<dyn Future + Send + 'async_trait>>, but I thought that's what the async fn was syntax sugar for??? Then I ran into this member-only medium post claiming

Rust Async Traits: What Finally Works Now

Async functions in traits shipped. Here’s what that means for your service interfaces.

But I clicked through every rust release since 1.75.0 where impl AsyncTrait was shipped and couldn't find a mention of async. Now I'm just confused (and still using async_trait). Hence the question above...

top 7 comments
sorted by: hot top controversial new old
[–] BB_C@programming.dev 6 points 2 days ago (1 children)

If I understand what you're asking...

This leaves out some details/specifics out to simplify. But basically:

async fn foo() {}

// ^ this roughly desugars to

fn foo() -> impl Future<()> {}

This meant that you couldn't just have (stable) async methods in traits, not because of async itself, but because you couldn't use impl Trait in return positions in trait methods, in general.

Box<dyn Future> was an unideal workaround (not zero-cost, and other dyn drawbacks). async_trait was a proc macro solution that generated code with that workaround. so Box<dyn Future> was never a desugaring done by the language/compiler.

now that we have (stable) impl Trait in return positions in trait methods, all this dance is not strictly needed anymore, and hasn't been needed for a while.

[–] Starfighter@discuss.tchncs.de 1 points 2 days ago* (last edited 2 days ago) (1 children)

I ran into the same issue not so long ago and at least for no_std I had to resort to using the async_trait crate. (The project is no_std but has alloc)

I can't recall the exact error so it might have been due to mixing async and non-async methods in the same trait. I would have to look at it again...

[–] BB_C@programming.dev 4 points 2 days ago

dyn compatibility of the trait itself is another matter. In this case, an async method makes a trait not dyn-compatible because of the implicit -> impl Future opaque return type, as documented here.

But OP didn't mention whether dyn is actually needed or not. For me, dyn is almost always a crutch (exceptions exist).

[–] TehPers@beehaw.org 2 points 2 days ago* (last edited 2 days ago)

Breaking down what async fn in trait does, it converts it to a fn method() -> impl Future<Output=T>, which breaks further down into fn method() -> Self::__MethodRet where the generated associated type implements Future.

This doesn't work for dyn Trait because of the associated type (and the fact method() needs a dyn-safe self parameter).

Instead, your methods need to return dyn Future in some capacity since that return type doesn't rely on associated types. That's where async_trait comes in. Box<dyn Future<...>> is a dyn-safe boxed future, then it's pinned because async usually generates a self-referential type, and you need to pin it anyway to .poll() the resulting future.

Edit: also, being pedantic, associated types are fine in dyn traits, but you need to specify the type for it (like dyn Blah<Foo=i32>. Even if you could name the return type from an async fn, it'd be different for every impl block, so that's not realistic here.

[–] cas@feddit.nl 3 points 2 days ago (1 children)

Oh I was in your position like A month ago... You're gonna love dealing with Dyn compatibility and Async. I lost half of my hair because I kept pulling on it and smashing my head against the wall

I'm tired, boss 🥲

[–] hallettj@leminal.space 1 points 2 days ago

I'm not informed on all the details, but a key difference between the async_trait macro and a native async keyword is that async_trait gives you that boxed, trait object type. IIUC the thinking is native support should not automatically box futures, which implies it shouldn't use dyn either. Using Box and dyn is an easy way to make sure the code works no matter what type of future a method returns. But the trade-off is some runtime overhead from heap allocation (due to Box), and dynamic dispatch (due to dyn).

According to areweasyncyet.rs:

async fn in trait method not stabilized yet

  • Workaround is available as an attribute macro: async-trait