Loading page…
Rust walkthroughs
Loading page…
Pin is a wrapper around a pointer type (like &mut T or Box<T>) that prevents the pointed-to value from being moved in memory. This is crucial for self-referential types and is the foundation of Rust's async/await system.
Key concepts:
Unpin trait — Types that can safely be moved even when pinned!Unpin — Types that must not be moved once pinnedMost types in Rust are Unpin, meaning they can be moved freely even through a Pin<&mut T>. The pin guarantees only matter for types that explicitly opt out with !Unpin (like compiler-generated futures).
When Pin matters:
Future trait manuallyuse std::pin::Pin;
fn main() {
let boxed = Box::new(42);
// Pin the Box - now the value cannot be moved
let pinned: Pin<Box<i32>> = Pin::new(boxed);
// We can still read the value
println!("Value: {}", *pinned);
// i32 is Unpin, so we can get a mutable reference
// (Only possible because i32 doesn't care about being moved)
let mut pinned = pinned;
let reference = pinned.as_mut().get_mut();
*reference = 100;
println!("Modified: {}", *pinned);
}use std::pin::Pin;
// Most types implement Unpin automatically
struct NormalStruct {
value: i32,
}
fn main() {
let mut boxed = Box::new(NormalStruct { value: 42 });
let mut pinned = Pin::new(boxed);
// NormalStruct is Unpin, so we can get mutable access
let reference = pinned.as_mut().get_mut();
reference.value = 100;
println!("Value: {}", reference.value);
// We can also unpin and move it
let unpinned: Box<NormalStruct> = pinned.into_inner();
let moved = unpinned; // Move is allowed!
println!("Moved: {}", moved.value);
}use std::pin::Pin;
use std::marker::PhantomPinned;
// A self-referential struct (simplified example)
// In practice, this is very hard to construct safely
struct SelfReferential {
data: String,
// This would be a reference to `data`
// In real code, this requires unsafe and careful construction
pointer: *const str, // Raw pointer to part of `data`
_marker: PhantomPinned, // Opt out of Unpin
}
impl SelfReferential {
fn new(s: &str) -> Pin<Box<Self>> {
let mut boxed = Box::new(SelfReferential {
data: s.to_string(),
pointer: std::ptr::null(),
_marker: PhantomPinned,
});
// Set up self-reference (unsafe!)
let self_ptr: *const str = &boxed.data;
unsafe {
let self_mut = Box::as_mut(&mut boxed) as *mut SelfReferential;
(*self_mut).pointer = self_ptr;
}
// Pin it before returning
Pin::from(boxed)
}
fn get_data(&self) -> &str {
&self.data
}
fn get_pointer(&self) -> &str {
// Safe because we know pointer points to valid data
unsafe { &*self.pointer }
}
}
fn main() {
let pinned = SelfReferential::new("hello");
println!("Data: {}", pinned.get_data());
println!("Pointer: {}", pinned.get_pointer());
// Cannot move or get mutable access because of PhantomPinned
// let moved = pinned; // This would be prevented by the type system
}use std::pin::Pin;
use std::future::Future;
use std::task::{Context, Poll};
// A simple future that completes after being polled once
struct OneShotFuture {
completed: bool,
}
impl Future for OneShotFuture {
type Output = &'static str;
fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
if self.completed {
Poll::Ready("done")
} else {
self.completed = true;
Poll::Pending
}
}
}
fn main() {
let mut future = Box::pin(OneShotFuture { completed: false });
// Create a context for polling (simplified)
let waker = futures::task::noop_waker();
let mut cx = Context::from_waker(&waker);
// Poll the future
match future.as_mut().poll(&mut cx) {
Poll::Ready(result) => println!("Ready: {}", result),
Poll::Pending => println!("Pending..."),
}
// Poll again
match future.as_mut().poll(&mut cx) {
Poll::Ready(result) => println!("Ready: {}", result),
Poll::Pending => println!("Pending..."),
}
}
// For the noop_waker, we need the futures crate or a custom implementation
mod futures {
use std::task::{Waker, RawWaker, RawWakerVTable};
pub fn noop_waker() -> Waker {
fn noop_clone(_: *const ()) -> RawWaker {
RawWaker::new(std::ptr::null(), &NOOP_VTABLE)
}
fn noop(_: *const ()) {}
static NOOP_VTABLE: RawWakerVTable = RawWakerVTable::new(
noop_clone, noop, noop, noop,
);
unsafe { Waker::from_raw(RawWaker::new(std::ptr::null(), &NOOP_VTABLE)) }
}
}use std::pin::Pin;
use std::marker::PhantomPinned;
struct PinnedStruct {
field1: String,
field2: i32,
_marker: PhantomPinned,
}
impl PinnedStruct {
fn new(s: &str, n: i32) -> Pin<Box<Self>> {
Pin::new(Box::new(Self {
field1: s.to_string(),
field2: n,
_marker: PhantomPinned,
}))
}
// Pin projection: project Pin<&mut Self> to Pin<&mut String>
// This is safe because field1 is structurally pinned
fn field1_pin(self: Pin<&mut Self>) -> Pin<&mut String> {
unsafe { self.map_unchecked_mut(|s| &mut s.field1) }
}
// field2 doesn't need pin projection since i32: Unpin
fn field2_mut(self: Pin<&mut Self>) -> &mut i32 {
unsafe { &mut self.get_unchecked_mut().field2 }
}
fn field1(&self) -> &String {
&self.field1
}
}
fn main() {
let mut pinned = PinnedStruct::new("hello", 42);
println!("field1: {}", pinned.field1());
println!("field2: {}", pinned.field2_mut());
// Modify fields through pinned access
pinned.as_mut().field1_pin().push_str(" world");
*pinned.as_mut().field2_mut() = 100;
println!("After modification:");
println!("field1: {}", pinned.field1());
println!("field2: {}", pinned.field2_mut());
}// This example shows how pin_project simplifies pin projections
// In Cargo.toml: pin_project = "1"
/*
use pin_project::pin_project;
#[pin_project]
struct MyStruct {
#[pin]
pinned_field: String, // This field is pinned
unpinned_field: i32, // This field is not pinned
}
impl MyStruct {
fn new() -> Pin<Box<Self>> {
Pin::new(Box::new(Self {
pinned_field: String::from("pinned"),
unpinned_field: 42,
}))
}
fn modify(self: Pin<&mut Self>) {
let this = self.project();
// pinned_field is Pin<&mut String>
this.pinned_field.push_str(" data");
// unpinned_field is &mut i32
*this.unpinned_field = 100;
}
}
fn main() {
let mut pinned = MyStruct::new();
pinned.as_mut().modify();
}
*/
// Manual example without pin_project:
use std::pin::Pin;
#[derive(Debug)]
struct MyStruct {
pinned_field: String,
unpinned_field: i32,
}
impl MyStruct {
fn new() -> Pin<Box<Self>> {
Pin::new(Box::new(Self {
pinned_field: String::from("pinned"),
unpinned_field: 42,
}))
}
}
fn main() {
let pinned = MyStruct::new();
println!("{:?}", *pinned);
}use std::pin::Pin;
fn main() {
// Pinning on the stack (requires unsafe to use correctly)
let mut value = 42;
// Create a pinned reference
let pinned = unsafe { Pin::new_unchecked(&mut value) };
// For Unpin types, this is safe
let reference = pinned.get_mut();
*reference = 100;
println!("Value: {}", value);
// For !Unpin types, stack pinning is dangerous
// because the value could be moved after pinning
}use std::pin::Pin;
struct MyFuture {
state: i32,
}
impl MyFuture {
fn new() -> Pin<Box<Self>> {
Box::pin(Self { state: 0 })
}
}
fn main() {
// Box::pin is the most common way to create pinned values
let pinned: Pin<Box<MyFuture>> = MyFuture::new();
// Common in async runtimes
let future = async {
42
};
let pinned_future = Box::pin(future);
println!("Future pinned");
}use std::pin::Pin;
use std::future::Future;
// Understanding how async generates !Unpin futures
async fn example_async() -> i32 {
let data = String::from("hello");
// This await point creates a state machine that may be !Unpin
// because it needs to store references across yield points
some_async_operation().await;
42
}
async fn some_async_operation() {
// Simulated async work
}
fn main() {
// The async block creates an anonymous type that is !Unpin
let future = example_async();
// Futures must be pinned before polling
let mut pinned = Box::pin(future);
println!("Future created and pinned");
}use std::pin::Pin;
fn main() {
let boxed = Box::new(42i32);
let pinned: Pin<Box<i32>> = Pin::new(boxed);
// as_ref: Pin<&T> from Pin<Box<T>>
let pin_ref: Pin<&i32> = pinned.as_ref();
println!("Ref: {}", *pin_ref);
let mut pinned = Pin::new(Box::new(42i32));
// as_mut: Pin<&mut T> from Pin<Box<T>>
let pin_mut: Pin<&mut i32> = pinned.as_mut();
// get_mut: &mut T (only for Unpin types)
let reference = pin_mut.get_mut();
*reference = 100;
// into_inner: Unwrap to get T (for Unpin) or Box<T>
let unpinned: Box<i32> = pinned.into_inner();
println!("Unpinned: {}", unpinned);
// set: Replace the value
let mut pinned = Pin::new(Box::new(42i32));
let old_value = pinned.as_mut().set(200);
println!("Old: {:?}, New: {}", old_value, *pinned);
}use std::pin::Pin;
use std::marker::PhantomPinned;
// A type that opts out of Unpin
struct NotUnpin {
_marker: PhantomPinned,
}
fn main() {
let boxed = Box::new(NotUnpin { _marker: PhantomPinned });
let pinned = Pin::new(boxed);
// Cannot call get_mut() because NotUnpin is !Unpin
// let reference = pinned.get_mut(); // Compile error!
// Cannot call into_inner() because NotUnpin is !Unpin
// let unpinned = pinned.into_inner(); // Compile error!
// To access a !Unpin type, you must use unsafe
let mut pinned = pinned;
unsafe {
let reference = pinned.as_mut().get_unchecked_mut();
// You must ensure the value is not moved!
let _ref = reference;
}
println!("Pinned value exists");
}use std::pin::Pin;
use std::future::Future;
use std::task::{Context, Poll};
use std::marker::PhantomPinned;
struct CountingFuture {
count: usize,
target: usize,
_marker: PhantomPinned, // Make it !Unpin
}
impl CountingFuture {
fn new(target: usize) -> Self {
Self {
count: 0,
target,
_marker: PhantomPinned,
}
}
}
impl Future for CountingFuture {
type Output = usize;
fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
// Safe because we don't move any pinned data
let this = unsafe { self.get_unchecked_mut() };
this.count += 1;
if this.count >= this.target {
Poll::Ready(this.count)
} else {
println!("Count: {}", this.count);
Poll::Pending
}
}
}
// A simple executor
fn block_on<F: Future>(mut future: F) -> F::Output {
use std::task::{RawWaker, RawWakerVTable, Waker};
let vtable = RawWakerVTable::new(
|_| RawWaker::new(std::ptr::null(), &VTABLE),
|_| {},
|_| {},
|_| {},
);
static VTABLE: RawWakerVTable = vtable;
let raw = RawWaker::new(std::ptr::null(), &VTABLE);
let waker = unsafe { Waker::from_raw(raw) };
let mut cx = Context::from_waker(&waker);
let mut future = unsafe { Pin::new_unchecked(&mut future) };
loop {
match future.as_mut().poll(&mut cx) {
Poll::Ready(val) => return val,
Poll::Pending => std::thread::yield_now(),
}
}
}
fn main() {
let future = CountingFuture::new(5);
let result = block_on(future);
println!("Final result: {}", result);
}Pin Types:
| Type | Description |
|------|-------------|
| Pin<P> | Wrapper preventing the pointee from moving |
| Pin<Box<T>> | Pinned heap allocation |
| Pin<&mut T> | Pinned mutable reference |
| Pin<&T> | Pinned shared reference |
Key Traits:
| Trait | Meaning |
|-------|---------|
| Unpin | Type can be moved even when pinned (auto-implemented for most types) |
| !Unpin | Type must not be moved once pinned (requires PhantomPinned) |
Key Methods:
| Method | Unpin Required? | Description |
|--------|-----------------|-------------|
| new(p) | No | Create Pin (for pointers) |
| new_unchecked(p) | No | Create Pin (unsafe, for references) |
| get_mut() | Yes | Get mutable reference |
| get_unchecked_mut() | No (unsafe) | Get mutable reference |
| into_inner() | Yes | Unwrap to inner pointer |
| set(value) | No | Replace the value |
| as_ref() | No | Get Pin<&T> |
| as_mut() | No | Get Pin<&mut T> |
When to Use Pin:
| Scenario | Pin Required? | |----------|---------------| | Working with async/await futures | ✅ Yes (implicitly) | | Self-referential structs | ✅ Yes | | Normal structs | ❌ Not needed (Unpin) | | Simple references | ❌ Not needed |
Key Points:
Pin<P> prevents the pointed-to value from being movedUnpin and can be moved freely!UnpinPhantomPinned marker opts a type out of Unpinunsafe and careful attentionBox::pin() is the safest way to create pinned valuespin_project crate for safe pin projection in structs