Reasoning About Async Rust with State Machines
Async functions in Rust are compiled into state machines by the compiler. Each await point creates a state where execution can yield, and variables used across await boundaries become struct fields. Understanding this transformation helps developers better reason about performance and correctness in async Rust code.
Background
This post is for Rust programmers who know the language basics but are trying to understand async/await — a way to write concurrent code that can pause and resume without blocking a thread. The key insight: Rust's `async fn` is not an ordinary function. The compiler transforms it into a **state machine** (a hidden enum where each variant represents a suspension point, plus a struct storing local variables that must survive across `.await` calls). Because the generated code implements the `Future` trait, its size and layout change when you add or remove `.await` calls, and ownership rules become visible as confusing compiler errors. Understanding this model helps developers reason about unexpected allocations and borrow-checker errors that only appear in async code.