Skip to content
Tech News
← Back to articles

Async Rust never left the MVP state

read original get Rust Programming Book → more articles
Why This Matters

This article highlights ongoing challenges with async Rust, particularly regarding binary size bloat on microcontrollers and other resource-constrained environments. Addressing these issues at the compiler level is crucial for making async Rust more efficient and truly zero-cost, enabling broader adoption across diverse hardware. The effort to optimize async code at the compiler level can significantly benefit both the industry and consumers by improving performance and reducing resource usage.

Key Takeaways

I've previously explained async bloat and some work-arounds for it, but would much prefer to solve the issue at the root, in the compiler. I've submitted a Project Goal, and am looking for help to fund the effort.

I love me some async Rust! It's amazing how we can write executor agnostic code that can run concurrently on huge servers and tiny microcontrollers.

But especially on those tiny microcontrollers we notice that async Rust is far from the zero cost abstractions we were promised. That's because every byte of binary size counts and async introduces a lot of bloat. This bloat exists on desktops and servers as well, but it's much less noticable when you have substantially more memory and compute available.

I've previously explained some work-arounds for this issue, but would much prefer to get to the root of the problem, and work on improving async bloat in the compiler. As such I have submitted a Project Goal.

This is part 2 of my blog series on this topic. See part 1 for the initial exploration of the topic and what you can do when writing async code to avoid some of the bloat. In this second part we'll dive into the internals and translate the methods of blog 1 into optimizations for the compiler.

What I won't be talking about is the often discussed problem of futures becoming bigger than necessary and them doing a lot of copying. People are aware of that already. In fact, there is an open PR that tackles part of it: https://github.com/rust-lang/rust/pull/135527

Anatomy of a generated future

We're going to be looking at this code:

fn foo() -> impl Future<Output = i32> { async { 5 } } fn bar() -> impl Future<Output = i32> { async { foo().await + foo().await } }

godbolt

... continue reading