Saltar a contenido

Week 11 - Async Foundations: Future, Pin, Unpin, the State Machine

11.1 Conceptual Core

  • A Future is a state machine with a poll method that returns Poll::Ready(T) or Poll::Pending. await is the only legal way to drive a future from inside another future.
  • async fn desugars to a function returning an anonymous type that implements Future. The compiler synthesizes the state machine from the control flow of the async block.
  • Pin<P> is a wrapper around a pointer P that promises the pointee will not be moved (in memory) until it is dropped-unless the pointee is Unpin. This is the linchpin that lets self-referential async state machines work.

11.2 Mechanical Detail

  • Why Pin? When an async function awaits, the compiler stores all locals (including references to other locals) in a single struct-the state. References to other fields of the same struct are self-references, valid only while the struct does not move. Without Pin, a Vec::push that re-allocates the state would invalidate them. Pin encodes the move-prohibition in the type system.
  • Unpin is an auto trait: a type is Unpin if it is safe to move even when pinned. Most types are Unpin. The exceptions: generators, async blocks, intrusive list nodes, self-referential structs.
  • pin! macro (stable since 1.68): pin a value on the stack ergonomically. Replaces tokio::pin! for std users.
  • The Waker: an opaque handle the runtime hands to a future so the future can signal "I'm ready to be polled again." Wakers are Clone + Send + Sync and have a wake() method.
  • Cancellation in async Rust = dropping the future. Every await point is a cancellation point. Code that holds a lock across an await and then cancels can deadlock if the lock is not async-aware. This is the single most common source of async bugs in production.

11.3 Lab-"An Async Channel From Scratch"

Implement a single-shot async oneshot channel: - Sender<T> has send(self, T). - Receiver<T> is Future<Output = Result<T, Cancelled>>. - Use a single Mutex<State> and a Waker slot. - Test with both tokio::test and smol::block_on. The result must be runtime-agnostic.

11.4 Idiomatic & Clippy Drill

  • clippy::async_yields_async, clippy::large_futures, clippy::unused_async. The middle lint flags futures whose state struct exceeds a kilobyte-a real performance hazard once you spawn millions.

11.5 Production Hardening Slice

  • Run your oneshot under tokio-console and console-subscriber. Confirm the future is dropped promptly on cancellation. Add tracing::instrument to every public async fn. Document the size of the generated futures with cargo +nightly rustc -- -Z print-type-sizes.

Comments