Async traits in Rust are finally stable. We are going to regret this.
Eight years after the first RFC, async fn in traits is stable. The ecosystem is about to learn what the lang team has been worried about.
By The Editors
The async-fn-in-trait saga began in 2018 with an RFC titled, charmingly, "this is fine." It ends — depending on how you count — this week, with the stabilization of async fn in trait definitions in Rust 1.88.
The lang team is celebrating. They have earned it.
What's actually shipping
You can now write:
trait Storage {
async fn get(&self, key: &str) -> Option<Vec<u8>>;
async fn put(&self, key: &str, value: Vec<u8>) -> Result<()>;
}
Without #[async_trait]. Without the macro. Without the box. The desugaring uses a hidden associated type, which means the trait is no longer object-safe by default — you need explicit dyn support via Box<dyn Storage> and a separate stable feature for that path.
Why this matters
The async-trait crate has been a load-bearing pillar of the Rust async ecosystem for half a decade. Every major async library uses it. The stabilization means library authors can finally drop a major dependency, simplify their public APIs, and stop apologizing for the macro in their docs.
The contrarian read
The new design solves the wrong problem first. The lang team picked clean syntax over object-safety because the syntax problem was the visible one — people complained about #[async_trait] every day, and almost nobody hit the object-safety limitation in practice.
Now the visible problem is gone, but the moment you write Box<dyn Storage> you re-enter macro country. The ergonomic story is "fast in the common case, weird in the trait-object case." That's a worse story than "consistent but with a macro everywhere," and library authors are about to discover it.
We're going to spend the next year reading clever blog posts about how to design APIs that work in both worlds. Some of them will be wrong. The Rust ecosystem will absorb the complexity, as it always does, by becoming slightly more arcane.
Worth it. Probably.
Related reading
Discussion
Comments live on GitHub Discussions via giscus. A GitHub account is required to post.
Comments are powered by giscus.
Configure
NEXT_PUBLIC_GISCUS_REPO_IDandNEXT_PUBLIC_GISCUS_CATEGORY_IDin.env.localto enable.