---
title: Internal Mechanisms of async/await
url: https://doc.liz6.com/en/rust/06-concurrency-and-asynchrony/02-async-await
locale: en
area: rust
tags:
- rust
- concurrency-and-asynchrony
date: 2026-06-30
modified: 2026-07-16
description: An async fn is desugared by the compiler into a state machine implementing the Future trait—each .await point is a state. Pin ensures the Future does not move in memory (self-referential structures), and Waker informs the executor when it can resume polling. Understanding this desugaring process is key to understanding Rust async’s "zero-cost abstraction" and why Futures cannot be dropped arbitrarily...
---

# Internal Mechanisms of async/await

> An `async fn` is desugared by the compiler into a state machine implementing the `Future` trait—each `.await` point is a state. `Pin` ensures the `Future` does not move in memory (self-referential structures), and `Waker` informs the executor when it can resume polling. Understanding this desugaring process is key to understanding Rust async’s "zero-cost abstraction" and why `Future`s cannot be dropped arbitrarily.

## What an `async fn` becomes after compilation

```rust
async fn read_config() -> Config {
    let file = read_file().await;      // await point 1
    parse(&file).await                 // await point 2
}
```

The compiler converts the `async fn` into an anonymous type implementing the `Future` trait—a state machine:

```rust
// Pseudo-code generated by the compiler:
enum ReadConfigFuture {
    State0 { ... },                    // Initial, about to call read_file
    State1 { file_content: String },   // Waiting for read_file to complete, holds file_content
    State2 { file: String },           // Waiting for parse to complete
    Done,
}

impl Future for ReadConfigFuture {
    type Output = Config;
    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Config> {
        match self {
            State0 => { /* start read_file, transition to State1 */ Poll::Pending },
            State1 { file_content } => { /* check if read_file done */ ... },
            // ...
        }
    }
}
```

Key point: Each `.await` point corresponds to a state machine transition. The state machine stores local variables that span across the await—they must remain alive after `poll()` returns `Pending`, until the next `poll()`.

## The Future trait

```rust
pub trait Future {
    type Output;
    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
}
```

The executor repeatedly calls `poll()`. If the `Future` is not yet complete, it returns `Poll::Pending`. Before returning `Pending`, the `Future` must register a waker via `cx.waker()`—a callback function that is invoked when the `Future` might become ready (e.g., I/O completion, timer expiration). Upon receiving a wake signal, the executor re-polls the `Future`.

## The role of Pin

State1 holds `file_content: String`. If the `Future` is moved to a new memory address, internal pointers holding that address (if any) would become dangling. For self-referential `Future`s (common in `select!` or scenarios involving borrowed locals), `Pin<&mut Self>` guarantees that the `Future` will not be moved—the compiler does not generate `memcpy` operations to move pinned values.

## Skeleton of a simple executor

```rust
fn block_on<F: Future>(mut future: F) -> F::Output {
    let waker = ...;                   // Create waker
    let mut cx = Context::from_waker(&waker);
    let mut future = unsafe { Pin::new_unchecked(&mut future) };
    loop {
        match future.as_mut().poll(&mut cx) {
            Poll::Ready(v) => return v,
            Poll::Pending => std::thread::park(),  // Wait for wake (production code uses epoll)
        }
    }
}
```

This is merely conceptual. Real-world executors (tokio, async-std) use epoll/kqueue/IOCP for I/O multiplexing, rather than busy-waiting.

## References

- **Rust Book**: Async chapter (currently in progress for official book)
- **Tokio tutorial**: tokio.rs/tokio/tutorial
- **async-book**: rust-lang.github.io/async-book

*Keywords: async, await, Future, Pin, poll, state machine, executor, waker*
