async and .await are the two halves of the syntax. Together they let you write code that looks sequential but yields the thread at each waiting point.
async makes a Future
An async fn doesn't run when you call it — it returns a Future, a value representing 'work that will produce a result later.' The body doesn't execute until something drives the Future to completion. This is the surprise for newcomers: calling an async function does nothing on its own.
.await drives and yields
.await on a Future does two things: it drives that Future toward completion, and — crucially — if the Future isn't ready (still waiting on I/O), it yields the current thread so other tasks can run, resuming later when the value is ready. The code reads top-to-bottom like blocking code, but under the hood each .await is a potential yield point.
.await (or a runtime) executes it. 'Why is my async function not running?' almost always means you built the Future but never awaited or spawned it.Futures are lazy and zero-cost
Rust's Futures are lazy (they do nothing until polled) and compile to efficient state machines — no heap allocation per await, no hidden runtime in the language itself. The compiler turns your async fn into a state machine the runtime polls. That's why async is zero-cost: you don't pay for a garbage-collected event loop baked into the language.