Pin and Unpin for safe self-referential async futures

Marcus Chen Jan 2026
1 tab
use std::pin::Pin;
use std::future::Future;

async fn example() {
    println!("Example future");
}

fn spawn_local<F>(future: F)
where
    F: Future<Output = ()> + 'static,
{
    let pinned = Box::pin(future);
    // Runtime would poll pinned future
}
1 file · rust Explain with highlit

Pin ensures that a value won't move in memory, which is required for self-referential types like async futures. Most async code hides this complexity, but understanding it helps when you hit compiler errors about Unpin. A type is Unpin if it's safe to move even when pinned; most types are Unpin by default. When you write async fn, the compiler generates a state machine that might borrow across .await points, making it !Unpin. You pin futures with Box::pin() or pin!(). I rarely use Pin directly in application code, but it's essential for writing async combinators, custom runtimes, or intrusive data structures. The guarantees enable memory-safe async without garbage collection.