What Pin actually guarantees
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.