๐ŸŽ“ A1Learner
๐Ÿ”

Rust Pin

Pin is a promise that a pinned value will not move again. Some futures point at their own insides while they run.

Most types are Unpin: moving them is always fine

Unpin is the normal case. Most types get it automatically: numbers, String, Vec. An Unpin value may still move. It may move even while it sits behind a Pin:

fn assert_unpin<T: Unpin>() {}
fn main() {
    assert_unpin::<i32>();
    assert_unpin::<String>();
    assert_unpin::<Vec<i32>>();
    println!("numbers, String and Vec are all Unpin");
}

poll: the question every future answers

Future::poll takes Pin<&mut Self> โ€” that signature is the whole reason Pin exists. poll also receives a Context: it carries the waker, the runtime callback that says this future is worth polling again:

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>
// Ready(value): finished โ€” here is the value
// Pending: not yet โ€” poll again after the waker fires

pin! pins a value on the stack

Box::pin(future) pins a future on the heap โ€” the standard way to store futures, and the closing program below uses it. The pin! macro pins a value on the stack when you need that instead:

use std::pin::pin;
fn main() {
    let fut = pin!(async { 7 });
    println!("pinned on the stack: {}", std::mem::size_of_val(&fut) > 0);
}

Pin<Box<T>> pins T, not the Box

The pointer itself may move freely, as long as the value it points to stays in its place:

let boxed = Box::pin(async { 42 });
let moved = boxed; // the Box changed hands โ€” that is fine
// the future it points to has not moved at all

Example

use std::future::Future; use std::pin::Pin;
fn main() {
    let fut = Box::pin(async { 42 });
    let pinned: Pin<Box<dyn Future<Output = i32>>> = fut;
    println!("future is pinned and ready for an executor: size known, address stable = {}", std::mem::size_of_val(&pinned) > 0);
}

Key points

  • Pin<P> wraps a pointer and promises: the value behind it will not move while the Pin exists
  • An async block that borrows across an .await can be self-referential, so it is not Unpin
  • Direct use of Pin is rare.
  • A move would break those self-pointers.

In this example, an async block that produces 42 is wrapped with Box::pin. The future now lives on the heap, so its address stays stable even if the Box itself is passed around. The value is typed as Pin<Box<dyn Future<Output = i32>>>. That is a pinned, boxed future. Its exact type is unknown. It will eventually give an i32. The println! confirms the pinned future exists and is ready to hand to an executor. Its address will not change โ€” that stability is the guarantee Pin stands for.

Note

Runtimes and combinators usually pin values for you.

Try it Yourself

Edit the code, then press Run.

Running is simulated ยท Rust Playground

Exercise

Why does Future::poll take Pin<&mut Self>?