Rust 1.75 made async trait methods stable; this 2026 cheat sheet maps Pin, pin-project, Send bounds, and driver-safe projections fast. Read now.

What Pin actually protects

Pin is a type-system contract that says a value’s address will not move for the rest of its life. That matters for async because many futures store self-references after the first poll: fields that point into sibling fields or into stack-like state that only stays valid while the future stays put. Without Pin, a move would invalidate those references and turn a compiler-checked program into undefined behavior at runtime. Pin does not freeze the value’s contents; it freezes its location. Interior mutability and projection still work when the API and the projection rules allow them.

In driver code you meet this whenever a type holds buffers, wakers, or completion state that must remain address-stable across poll cycles. Treat Pin as part of the type’s public contract, not as a decoration you add after the fact. If a future or stream is !Unpin, callers must keep it pinned; if it is Unpin, Pin is a no-op wrapper and ordinary moves remain safe.

Async traits after stabilization

Stable async trait methods remove the old need to box every future or invent ad-hoc associated-type factories for common interfaces. Methods can return impl Future directly from the trait, which keeps call sites readable and lets the compiler monomorphize concrete futures when types are known. The tradeoff is lifetime and bound pressure: each async method is still a state machine, and trait objects or dynamic dispatch may force boxing and explicit Send/Sync requirements that static dispatch could hide.

For drivers and I/O traits, prefer async methods when the operation is naturally asynchronous and the trait is used with concrete types. Reach for associated Future types or manual pin projections when you need tighter control over allocation, cancellation, or how state is split across fields. Stabilization simplified the common path; it did not erase the harder cases where the future’s shape is part of the design.

Send bounds and pin-project in practice

Send on a future means the whole state machine can move between threads. Any non-Send field—raw handles, thread-local state, or types that are only valid on one executor—makes the future !Send and can break spawn or work-stealing runtimes. When you write async trait methods used from multi-threaded executors, audit every captured field and every temporary held across an await. If a type only needs to be polled on one thread, document !Send deliberately instead of letting bounds fail deep in call stacks.

  • Use pin-project (or equivalent safe projection) when a struct mixes pinned and unpinned fields and you must project &mut or Pin<&mut> without unsafe boilerplate.
  • Project only the fields that must stay pinned; leave Unpin fields as ordinary references so callers are not over-constrained.
  • Keep poll implementations short: project, advance the inner future or state, store wakers, return Pending or Ready without moving pinned storage.
  • Prefer structural pinning for driver state machines; avoid address-dependent tricks that cannot be expressed with Pin and safe projections.

Driver-safe projections

A driver-safe projection keeps the Pin invariant end to end: you never obtain an unpinned &mut to a field that must not move, and you never move out of a pinned place. Macros and helpers exist so you do not hand-roll unsafe every time you touch nested futures. When projecting into an async trait future held inside a larger struct, pin the outer value first, then project inward so each nested future remains pinned for its poll.

Practical checklist: mark only the fields that need address stability as structurally pinned; implement Unpin manually only when every pinned field is Unpin or never used under Pin; test cancellation paths so Drop runs with the same layout poll assumed; and keep Send bounds aligned with where the future will be polled. Used this way, Pin, pin-project, Send bounds, and stable async traits form a small, repeatable pattern for correct async drivers rather than a collection of unrelated edge cases.

Automate Your Content with AI Video Generator

Try it Free →