Advanced Lifetimes and Borrowing
Pin<P>
std::pin::Pin<P> is a #[repr(transparent)] wrapper around a pointer type P (where P: Deref, such as &T, &mut T, Box<T>, Rc<T>, or Arc<T>) that restricts mutable access to the pointee P::Target whenever P::Target does not implement Unpin.
Notice That Pin<P> Wraps the Pointer, Not the Value
You never write Pin<MyFuture>; you always wrap a pointer or reference to the value: Pin<&mut MyFuture> or Pin<Box<MyFuture>>. Because Pin<P> is #[repr(transparent)] over P, it has zero memory or runtime overhead.
Pin<Box<T>> Layout (8 bytes on 64-bit targets — identical to Box<T>):
+------------------------------------+ +----------------------------+
| Pin<Box<T>> | | Heap Allocation (Pinned T) |
| pointer: Box<T> -----------------+------> | Address is permanently |
+------------------------------------+ | fixed if T: !Unpin |
+----------------------------+
Key Methods on Pin<P>
| Method | Signature | Safe or Unsafe? | Behavior |
|---|---|---|---|
Pin::new(pointer) |
fn new(pointer: P) -> Pin<P> where P::Target: Unpin |
Safe (requires Target: Unpin) |
Wraps any pointer to an Unpin type |
Pin::new_unchecked(pointer) |
unsafe fn new_unchecked(pointer: P) -> Pin<P> |
Unsafe (for !Unpin types) |
Asserts that the pointee will never be moved again |
Box::pin(val) |
fn pin(x: T) -> Pin<Box<T>> |
Safe (works for all T) |
Allocates x on the heap and pins it immediately |
.as_ref() |
fn as_ref(&self) -> Pin<&P::Target> |
Safe | Borrows Pin<Box<T>> or Pin<&mut T> as Pin<&T> |
.as_mut() |
fn as_mut(&mut self) -> Pin<&mut P::Target> |
Safe | Reborrows Pin<Box<T>> or Pin<&mut T> as Pin<&mut T> without consuming the owner |
.get_mut() |
fn get_mut(self) -> &'a mut T where T: Unpin |
Safe (requires T: Unpin) |
Unwraps Pin<&'a mut T> into &'a mut T |
.get_unchecked_mut() |
unsafe fn get_unchecked_mut(self) -> &'a mut T |
Unsafe | Returns &'a mut T for a !Unpin type (caller must not move *self!) |
.set(value) |
fn set(&mut self, value: P::Target) |
Safe | Drops the current pinned value in place and overwrites the slot with value |
Structural Pin Projection
When a struct is pinned (self: Pin<&mut Self>), accessing its fields requires deciding for each field whether that field is structurally pinned (Pin<&mut Field>) or not structurally pinned (&mut Field):
use std::marker::PhantomPinned;
use std::pin::{Pin, pin};
struct TaskFrame {
// Not structurally pinned: ordinary `u32` counter that can be mutated via `&mut u32`:
polls: u32,
// Structurally pinned: address-sensitive buffer:
buffer: String,
_pin: PhantomPinned,
}
impl TaskFrame {
// Project `self: Pin<&mut Self>` into `(&mut u32, Pin<&mut String>)`:
fn project(self: Pin<&mut Self>) -> (&mut u32, Pin<&mut String>) {
// SAFETY: We do not move `self`; `polls` is not pinned (`&mut u32`), and
// `buffer` is projected as `Pin<&mut String>`.
unsafe {
let this = self.get_unchecked_mut();
(&mut this.polls, Pin::new_unchecked(&mut this.buffer))
}
}
}
fn main() {
let mut frame = pin!(TaskFrame {
polls: 0,
buffer: String::from("frame"),
_pin: PhantomPinned,
});
let (polls, mut pinned_buf) = frame.as_mut().project();
*polls += 1;
pinned_buf.push_str("-active");
println!("polls: {}, buffer: {:?}", frame.polls, frame.buffer);
}
polls: 1, buffer: "frame-active"