Week 11 - Async Foundations: Future, Pin, Unpin, the State Machine¶
11.1 Conceptual Core¶
- A
Futureis a state machine with apollmethod that returnsPoll::Ready(T)orPoll::Pending.awaitis the only legal way to drive a future from inside another future. async fndesugars to a function returning an anonymous type that implementsFuture. The compiler synthesizes the state machine from the control flow of the async block.Pin<P>is a wrapper around a pointerPthat promises the pointee will not be moved (in memory) until it is dropped-unless the pointee isUnpin. 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. WithoutPin, aVec::pushthat re-allocates the state would invalidate them.Pinencodes the move-prohibition in the type system. Unpinis an auto trait: a type isUnpinif it is safe to move even when pinned. Most types areUnpin. The exceptions: generators, async blocks, intrusive list nodes, self-referential structs.pin!macro (stable since 1.68): pin a value on the stack ergonomically. Replacestokio::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 areClone + Send + Syncand have awake()method. - Cancellation in async Rust = dropping the future. Every
awaitpoint is a cancellation point. Code that holds a lock across anawaitand 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-consoleandconsole-subscriber. Confirm the future is dropped promptly on cancellation. Addtracing::instrumentto every public async fn. Document the size of the generated futures withcargo +nightly rustc -- -Z print-type-sizes.