p.enthalabs

What is `std::pin::Pin` in Rust?

`std::pin::Pin` is a pointer wrapper that represents the guarantee that the pointee will not be moved through that pointer.

Why is it required?

The need for pinning arises from self-referential types. Self-referential structs are the most common example of address-sensitive types, and are the primary motivation for `Pin`. Take the following struct for example:

``` 1struct SelfRef { 2 data: i32, 3 ptr: *const i32, // points to self.data 4} ```

If we move an instance of this struct (for example, by moving ownership into another variable or returning it), its memory address changes. However, the raw pointer `ptr` still refers to the old memory location, leaving a dangling pointer. Hence, we need a way to mark `SelfRef` as unsafe to move once those self-references have been established, so any API that touches it must respect that. Rust cannot physically prevent moves. Instead, `Pin` encodes this as a type-level guarantee, which the rest of this post unpacks.

Where do we see this problem?

We see this most commonly with `async`/`await` and `Futures`.

Local variables that live across an `.await` point become fields in the compiler-generated state machine. If a reference to one local variable also lives across the same `.await`, the generated future becomes self-referential.

Once polling begins, the future may rely on internal references that point to other fields within itself. Moving the future afterward would invalidate those references. To prevent this, the `Future::poll` method requires the future to be pinned:

``` 1pub trait Future { 2 type Output; 3 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>; 4} ```

By taking `Pin<&mut Self>` instead of `&mut self`, callers of `poll` are required to guarantee that the future will not be moved after polling begins.

How does pinning work?

`Pin<P>` prevents safe code from moving the pointee through that pointer while still allowing ordinary mutation of the pinned value.

The Problem with `&mut T`

If you have a mutable reference `&mut T`, functions like `mem::replace`, `mem::swap`, or assignment can relocate the value stored at that memory location.

`Pin` restricts recovering an ordinary mutable reference. Safe code cannot recover an ordinary `&mut T` from a `Pin<&mut T>` unless `T: Unpin`.

``` 1impl<'a, T: ?Sized> Pin<&'a T> { 2 pub const fn get_ref(self) -> &'a T { ... } 3} 4 5impl<'a, T: ?Sized> Pin<&'a mut T> { 6 pub const fn get_mut(self) -> &'a mut T 7 where 8 T: Unpin 9 { ... } 10} ```

Important

`Pin` only prevents moves through the pinned pointer. It does not prevent mutation of the pinned value. Methods on the pinned type may freely mutate its fields, provided they do not move them.

If the type does not implement `Unpin` (i.e., it is `!Unpin`), you cannot get `&mut T` using safe code. You must use `unsafe` methods like `Pin::get_unchecked_mut`, indicating to the compiler that you promise not to move the value out of that reference.

What is `Unpin`?

A type implementing `Unpin` does not rely on pinning for soundness.

``` 1// std::marker 2pub auto trait Unpin {} ```

Most types in Rust (like `i32`, `String`, `Vec`, etc.) do not care about being moved and are `Unpin` by default. `Unpin` is implemented for all types automatically unless `!Unpin` is explicitly implemented.

Tip

The marker struct `std::marker::PhantomPinned` is explicitly `!Unpin`. Because auto-traits propagate automatically, any struct containing a `PhantomPinned` field is also automatically `!Unpin`.

``` 1use std::marker::PhantomPinned; 2 3struct SelfRef { 4 data: i32, 5 ptr: *const i32, 6 _phantom: PhantomPinned, // makes the entire struct !Unpin 7} ```

This is the standard way to declare that a custom struct is unsafe to move after being pinned. On stable Rust, it is also the _only_ way: writing `impl !Unpin for MyType {}` directly requires the `negative_impls` feature, which is only available on nightly. `PhantomPinned` exists as a public marker so that opting out of `Unpin` is possible on stable.

Because the compiler cannot automatically detect self-references (which are typically created using unsafe raw pointers), it cannot automatically mark such structs as `!Unpin`.

Therefore, this relies on a contract: the developer must explicitly opt out of `Unpin` (typically by embedding a `PhantomPinned` field) for any self-referential struct. If a self-referential type mistakenly remains `Unpin`, safe code can recover a mutable reference from a `Pin` and move the value, violating the assumptions made by the unsafe code that created the self-reference.

`Pin` does not physically prevent values from moving. Instead, it is a type-level guarantee that the value will not be moved through that pointer. Constructing a `Pin` safely therefore requires ensuring that the pointee will remain at a stable memory location for the lifetime of that pin.

Constructing a `Pin`

`Pin` itself does not pin a value. Instead, constructing a `Pin` means proving that the pointee will remain at a stable memory location for the lifetime of that pin.

`Pin::new`

The simplest way to construct a Pin is with `Pin::new`:

``` 1let mut value = 42; 2let pinned = Pin::new(&mut value); ```

However, this constructor is only available when `T: Unpin`.

Since `Unpin` types do not rely on pinning for soundness, wrapping them in a `Pin` is always safe. The pinning guarantee is effectively a no-op.

`pin!`

When you need to pin a value locally without allocating on the heap, you can use the `pin!` macro:

``` 1use std::pin::pin; 2 3let future = pin!(async { 4 println!("Hello"); 5}); ```

The macro creates a local variable and returns a `Pin<&mut T>` referring to it. The compiler ensures that the local variable will not be moved for the remainder of its lifetime, making this a safe way to pin `!Unpin` values on the stack.

Warning

Despite the name, `pin!` does not pin the stack memory itself. It creates a pinned reference whose lifetime is tied to the local variable. Once the variable goes out of scope, the pinning guarantee ends.

`Box::pin`

For `!Unpin` types, the most common constructor is `Box::pin`:

`1let pinned = Box::pin(SelfRef { ... });`

Unlike `pin!`, which creates a `Pin<&mut T>` tied to a local variable, `Box::pin` returns a `Pin<Box<T>>` whose lifetime is owned by the `Box`. Because the heap allocation itself does not move, the pointee has a stable memory location for the lifetime of the `Box`, allowing it to be safely pinned.

Note

Moving the `Box` itself does not move the value it owns. Only the pointer stored inside the `Box` is moved. The heap allocation remains at the same address.

`Pin::new_unchecked`

Sometimes safe constructors cannot prove that a value will remain in place. In these cases, unsafe code can construct a `Pin` manually:

`1let pinned = unsafe { Pin::new_unchecked(ptr) };`

By calling `Pin::new_unchecked`, the caller promises that the pointee will never again be moved through any pointer for the lifetime of the resulting `Pin`. If this promise is violated, any code relying on the pinning guarantee may exhibit undefined behavior. Because of this, `Pin::new_unchecked` is typically used only when implementing low-level abstractions that can uphold this invariant.

When do you actually need to care?

For most Rust developers, `Pin` and `Unpin` operate quietly in the background. You generally only need to think about them in two scenarios:

1. **Consuming async code:** If you need to manually poll a future or pass a future to an API that requires a pinned future, reach for `Box::pin(future)` (to pin it on the heap) or `std::pin::pin!(future)` (to pin it locally on the stack).

2. **Implementing `Future` by hand:** If you’re writing a custom state machine or another low-level async primitive, you’ll need to work with `Pin<&mut Self>` and may need to use `PhantomPinned` and unsafe code to uphold pinning invariants.

Ultimately, `Pin` is Rust’s zero-cost solution to the problem of address-sensitive types. It enables ergonomic `async`/`await` and other self-referential abstractions while preserving Rust’s memory safety guarantees without requiring a garbage collector.