ketchalegend
← Back

Async Rust Compiler Optimizations Still Missing in 2026

A deep dive into the claim that async Rust remains unfinished, exploring community reactions and what it means for builders.

A recent analysis on Tweedegolf argues that async Rust's compiler optimizations remain incomplete. The post sparked quiet but pointed discussion on Hacker News about the gap between the language's promise and its current performance reality.

The Case for Missing Optimizations

The author walks through several examples where the Rust compiler fails to optimize trivial async patterns. For instance, a simple async fn that immediately awaits a future may generate unnecessary allocations or state machine bloat. Such cases could be transformed into synchronous code by the compiler, but it doesn't. Issues like Pin boxing overhead in basic combinators and missing inlining opportunities are cataloged with assembly output backing each claim.

Consider a trivial async wrapper:

async fn wrap() -> i32 {
    let x = async { 42 }.await;
    x
}

Ideally, the compiler would inline and eliminate the async block entirely. But as of recent stable versions, it may still create a poll state machine that costs more than a simple return. Similar cases appear with Box::pin overhead in combinators like join! when a single future is passed.

For a deeper understanding of async Rust fundamentals, refer to the Async Book and the Rust Reference on await expressions.

Community Sentiment

The Hacker News discussion, with 35 points and a handful of comments, shows measured agreement. One commenter wrote: "I've felt before that compilers often don't put much effort into optimizing the 'trivial' cases." Another added: "Overly dramatic title for the content, though. I would have clicked 'Async Rust Optimizations the Compiler Still Misses' too you know." The consensus is that the content is solid, even if the title was provocative. Many developers have encountered these issues themselves.

Practical Tips for Optimizing Async Code

For builders writing async Rust today, manual optimization is often necessary. The compiler won't automatically handle many cases. Apply these strategies:

  • Avoid unnecessary Box::pin. Use pin-project or tokio::pin! to stack-allocate when possible.
  • Profile your async code. Tools like perf or tokio-console help spot unexpected allocations.
  • Simplify your futures. Break large state machines into smaller ones the compiler can inline.
  • Monitor nightly tracking issues. Optimizations like async_fn_in_trait and performance improvements are in progress.

For example, instead of boxing a future just to pass it to a combinator, use Tokio's pin! macro:

use tokio::{pin, select};

let fut = async { /* ... */ };
pin!(fut);
select! {
    _ = &mut fut => { },
    // ...
}

This keeps the future on the stack, avoiding heap allocation. See the Tokio documentation for more patterns.

The Road Ahead

Async Rust is still young compared to Go's goroutines or JavaScript's event loop. The compiler team has focused on correctness and safety; optimizations often come later. However, the gap between promise and reality is real. When you write async fn fetch() -> Data, you expect efficiency, but the compiler may insert hidden overhead. This isn't unique to Rust — even C++ coroutines had similar issues early on. Many omissions are known and tracked, but progress is slow.

Takeaway

If you're building latency-sensitive services (HTTP servers, real-time pipelines), every allocation counts. The compiler's lack of optimization can hurt. For batch scripts or tools where throughput isn't critical, async's MVP state is acceptable. Library authors should be explicit about performance characteristics of their async APIs — don't assume the compiler will clean up after you. Stay vigilant, profile your code, and apply manual optimizations where needed.

For ongoing discussions, follow the Rust issue tracker and the async performance tracking issue.