Theme
Q063. Why do Pin and Unpin exist in async Rust?
Short Answer
Some futures can contain references to their own fields after they begin execution, so moving them could invalidate those references. Pin<P> lets code promise that the pointee will not move again through that pointer. Future::poll receives Pin<&mut Self> so self-referential or address-sensitive futures can be polled safely. Unpin marks types that do not rely on this guarantee and may still be moved normally.
Deep Dive
Async state machines may store both an awaited child future and state that refers into the same parent allocation. Rust does not expose those generated self-references directly, but the polling API must support such layouts.
Pinning restricts safe access; it does not physically attach memory to the stack or heap. Box::pin(value) places a value in a heap allocation and returns Pin<Box<T>>. A stack value can also be pinned for a limited scope. Moving the pointer is fine because the pointee's address stays fixed.
Most ordinary types implement Unpin automatically. For them, Pin<&mut T> can be treated much like &mut T. Pinning matters when a type is !Unpin or when projecting pinned fields in custom future implementations.
Internal Model
Pin is a library type with compiler-supported Unpin auto-trait behavior. The address guarantee is upheld by API restrictions and unsafe-code contracts, not by runtime tracking.
The first poll is the point after which an address-sensitive future may establish internal references. Executors pin task futures before polling them.
Example
rust
use std::pin::Pin;
#[tokio::main]
async fn main() {
let future = async {
tokio::task::yield_now().await;
42_u64
};
let pinned: Pin<Box<_>> = Box::pin(future);
assert_eq!(pinned.await, 42);
}Common Mistakes
- Saying all futures are stored on the heap.
- Believing pinning prevents moving the
Boxor pointer handle. - Using unsafe pin projection without proving that pinned fields never move.
Follow-ups
- What does
Unpinmean, and why do most types implement it? - Can a value be pinned on the stack?
- Why is projecting
Pin<&mut Struct>to a field non-trivial?
Related
- Q056. What is a
Future? - Q059. How does
Future::pollwork? - Q062. What happens internally at an
.awaitpoint?
References
- Rust Standard Library
- Async Book
- Rustonomicon