DEV Community

Cover image for Rust's async story is still an embarrassment for web APIs
Aditya Agarwal
Aditya Agarwal

Posted on

Rust's async story is still an embarrassment for web APIs

I came to Rust expecting the memory safety and performance everyone raves about, and for a while it delivered. Then I tried writing async Rust, and all that confidence in my code evaporated.. All of a sudden, I had to fight Pin, decipher Waker, and choose among three runtimes, before I had even processed a single request. 😮😨

The language is great. The async story is not.

I want to emphasize my position. Rust was the first mainstream language to achieve memory safety in this manner. However, we must acknowledge that there is a deficiency in the ecosystem that no one dares to speak of openly. A programming language can be great and yet possess a shameful shortcoming. The gap lies in async for web work. By acting as if there is no problem, the adoption among the exact CRUD-and-endpoints crowd that Rust aims to attract is slowed.

Pin, Waker, and the three-runtime shuffle

You can read any developer forum thread on Pin, Waker, and fragmented runtimes. The same confusion every iteration. Implementing a Future by hand involves a lot of Pin<&mut Self> wrestling. It quickly boxes you into unsafeland or a macro-heavy crate like pin-project just to keep going. And these weren't complaints from newcomers either. withoutboats, a Rust contributor, made a late 2024 push for pinning to be a first-class language feature, citing the existing library solutions as an "ergonomic challenge" and an "annoying footgun" which gets "in the way of Drop". Also, there's the runtime split e.g. tokio, async-std, smol. You choose one, your entire dependency tree inherits the choice.

Multi-threaded by default was the wrong default

Maciej Hirsz, the author of the crates logos and beef, put it clearly. He called it "The Original Sin of Rust async programming is making it multi-threaded by default."

That default curses your code with Send + 'static, or as he says, "worse yet Send + Sync + 'static." If you've ever chased a trait bound error at 2am, you know the feeling. Here's the painful bit. More than 20,000 crates rely on Tokio as of mid-2026. That hard-couples axum, reqwest, and sqlx to one runtime. You never pick Tokio. The moment you wish for a web framework, an HTTP client, and a database driver in a project - it's given to you. → Web framework? Tokio. → HTTP client? Tokio. → Database driver? Tokio.

The receipts are piling up

They are reflected in migration schedules and recruitment strategies. It was revealed in an Airtable postmortem in early 2026, that the database core rewrite from Node.js/TypeScript to Rust spent approximately three years to be production ready. Three years. Discord had to transfer their Read States service to Rust and even use the nightly version since the async ergonomics were very tough when attempting to implement it in the stable version. And survey data supports this. According to the 10th Annual State of Rust Survey, 22% of non-users cited "too difficult to learn" as their reason. Even more damaging to potential Rust on the web adoption is that the survey found 36% of organizations had no plans to hire Rust developers whatsoever. This is a good chunk of the target audience just leaving the room. The survey's respondents cited the same reasons: they had no plans to use Rust because the ramp-up for CRUD and web apps is still too steep right now.

To be fair, they know

The team is not ignoring the problem. The Rust 2025H1 Project Goals is in fact a direct attempt to address the async pain and it clearly mentions that it should "bring the Async Rust experience closer to parity with sync Rust."

Part of that is having a complete story for async fn in traits so that #[async_trait] can finally retire. That's progress, and I'm hopeful it will happen. However, intention doesn't come with ergonomics. As of now, the bridge between "Rust is safe" and "Rust is enjoyable for a web API" is broad enough to let people slip through.

The takeaway

Rust became known due to its memory safety features. However, it hasn't really established itself as having a simple async functionality, which is particularly noticed by web developers. To truly care about a programming language, we must be willing to acknowledge its shortcomings rather than justify or defend them. 🦀

So here's my question for you: would you feel like starting a brand-new web API in Rust today, or would you rather go with something having a boring, boring async story?

Top comments (1)

Collapse
 
unitbuilds profile image
UnitBuilds

Well if we truly are about a programming language, we should fix it? My opinion lately has always pointed towards 'if it's not perfect, make it perfect. If you cant, build your own'. That's how I ended up down the rabbit hole of creating a rust-msi crate, because the literal "rust-msi' crate, doesnt actually create msi files that windows supports, OSE 4 vs OSE 3, great for reading and writing it's own packages, but what's the point if windows doesnt natively support it? How do you want to do deployment installs organization wide, if the msi file cant execute?

Thanks for bringing up the async dilemma, I think I'm gunna have a look into it, see if I cant come up with a solution to it. I've been using tokio for so long, that at this point, they might as well include it in the source... And that's a problem. Reliance on a single package is almost always using a hammer for spanner and its never a perfect solution.

For your question I'd sooner write a new API, so I can keep the codebase clean and easier to maintain. Rust's compiler warnings are fantastic, but tracking down a real Rust bug is a pain...