Essential Standard Traits

Unpin

The Unpin trait in std::marker is an auto trait indicating that a value can be safely moved in memory even after being wrapped in a Pin<P> pointer.

Trait Definition

pub auto trait Unpin {}

Most types in Rust have no internal pointers into their own fields, so moving them from one memory address to another with memcpy is always safe. Because Unpin is an auto trait, almost every standard type (i32, String, Vec<T>, Box<T>, references) implements Unpin automatically.

Why Pin<P> and Unpin Exist

Compiler-generated async state machines can hold references to their own local variables across .await points, making them self-referential structs. Moving a self-referential struct in memory invalidates its internal pointer:

Before Move (at 0x1000)             After Move to 0x2000 (Invalid!)
+-----------------------+           +-----------------------+
| data: "hi"  (0x1000)  | <---+     | data: "hi"  (0x2000)  |
| ptr: 0x1000           | ----+     | ptr: 0x1000 (dangling)| ──> 0x1000
+-----------------------+           +-----------------------+

Pin<P> prevents obtaining a &mut T to the pinned value unless T: Unpin.

Behavior of Pin<&mut T> Based on Unpin

Does T Implement Unpin? Examples Effect of Pin<&mut T>
Yes (T: Unpin) i32, String, Vec<T>, most structs Pin<&mut T> implements DerefMut<Target = T>; Pin::new(&mut val) and Pin::into_inner(pin) work in safe code
No (!Unpin) async blocks/futures, PhantomPinned Pin<&mut T> withholds safe &mut T access so the caller cannot call std::mem::swap or std::mem::replace to move T

Opting Out of Unpin with PhantomPinned

To mark a custom struct as !Unpin, include a std::marker::PhantomPinned field:

use std::marker::PhantomPinned;
use std::pin::Pin;

struct NormalBuffer {
    data: String,
}

struct PinnedNode {
    label: String,
    _pin: PhantomPinned, // Makes PinnedNode !Unpin
}

fn main() {
    // 1. NormalBuffer is Unpin -> Pin::new works in safe code and allows DerefMut:
    let mut normal = NormalBuffer {
        data: String::from("movable"),
    };
    let mut pinned_normal = Pin::new(&mut normal);
    pinned_normal.data.push_str(" (mutated)");
    println!("NormalBuffer: {}", pinned_normal.data);

    // 2. PinnedNode is !Unpin -> must be pinned on the heap via Box::pin:
    let node: Pin<Box<PinnedNode>> = Box::pin(PinnedNode {
        label: String::from("address-stable"),
        _pin: PhantomPinned,
    });
    println!("PinnedNode:   {}", node.label);
}
NormalBuffer: movable (mutated)
PinnedNode:   address-stable