pin-project
Safe, ergonomic pin projection for Rust structs and enums
Repository Health
Technical Analysis
pin-project generates safe projection types for Pin<&mut Self> fields via a #[pin_project] attribute macro, so authors of hand-written Future/Stream/AsyncRead implementations don’t have to write unsafe code to access pinned fields. Marking a field #[pin] produces a .project() method returning Pin<&mut T> for pinned fields and plain &mut T for the rest, matching exactly what the borrow checker needs.
It’s the de facto standard for pin projection in the Rust async ecosystem, used directly or transitively by a huge share of futures/stream-combinator crates; a lighter-weight sibling, pin-project-lite, trades some ergonomics for a declarative-macro-only implementation with no proc-macro compile-time cost.
What You Get
- A
#[pin_project]attribute macro that generates safe projection types for structs and enums - Per-field
#[pin]markers controlling which fields need pinned access - Named enum projections via
#[pin_project(project = EnumProj)]for pattern-matching on pinned enum variants #[project_replace]support forDrop-aware in-place field replacementno_stdandno_alloccompatibility for embedded/constrained async runtimes
Common Use Cases
- Implementing a custom
FutureorStreamby hand that wraps an inner pinned future - Writing
AsyncRead/AsyncWriteadapters that need pinned access to an inner reader/writer - Building async combinator types (map, then, select) where some fields must stay pinned across
pollcalls - Avoiding manual
unsafe impl Unpinbookkeeping when only some struct fields are self-referential
Under The Hood
Architecture: The public crate (src/lib.rs, 352 lines) is a documentation/re-export shim over the actual proc-macro implementation in the sibling pin-project-internal crate, which parses the annotated struct/enum and emits a projection type plus the .project()/.project_ref() methods along with the compiler-checked unsafe impl<...> Unpin for ... bounds needed to make the projection sound.
Tech Stack: Pure Rust, no_std/no_alloc compatible (categories include no-std, no-std::no-alloc in Cargo.toml), dual-licensed Apache-2.0/MIT, maintained by taiki-e who owns a large share of foundational async/no_std Rust tooling (also pin-project-lite, cargo-hack).
Code Quality: 140+ test files under tests/ cover struct/enum projection, Drop interaction, !Unpin edge cases, and macro-expansion snapshots, with examples/*-expanded.rs files showing exactly what code the macro generates for auditability — an important property for a crate whose entire value proposition is proving generated unsafe code is sound.
API Design: The core API is two annotations (#[pin_project] on the type, #[pin] on individual fields) plus a generated .project() call — deliberately mirroring how you’d naturally reason about which fields need pinning, with enum support requiring only one extra project = Name argument, keeping the mental model close to plain Rust pattern matching.