weichengyi.com

What Pin actually guarantees

27 April 2026 · rust

Pin<P> guarantees one thing: that the pointee will not be moved in memory before it is dropped. Not that it is immutable. Not that it is heap-allocated. Not that it is thread-safe. It will not move.

And that guarantee only binds for types that are !Unpin. For an Unpin type — which is nearly everything you write by hand, since the trait is auto-derived — Pin imposes no restriction whatsoever, and Pin::new and get_mut are both safe. This is why Pin feels vacuous the first few times you meet it: on ordinary types it is vacuous.

The problem it exists for

An async fn compiles to a generated state machine, and that state machine can hold a reference into its own storage — a borrow held across an await point. That makes it self-referential: the struct contains a pointer into itself. Move it, and the pointer dangles, with no borrow-checker error because the compiler cannot see through the generated type.

So the generated futures are !Unpin, and Future::poll takes self: Pin<&mut Self>. The type system now refuses to hand you a &mut you could feed to mem::swap.

The part that surprises people

Pinning is a property of the pointer, not of the value. The value does not know it is pinned and has no marker. Pin is a wrapper that withholds &mut access, and that withholding is the entire mechanism.

Which is also why Pin::new_unchecked is unsafe: you are asserting a property about the future history of that memory that the compiler cannot check, and nothing will tell you if you are wrong.