Rust 1.33 gives address-sensitive values a pinning contract
Pin restricts the operations available through a pointer so address-sensitive values can remain in place. The contract also covers storage lifetime, destructors and access to fields.
Putting a value on the heap does not by itself stop code from moving that value out again. A Box<T> owns an allocation, but ordinary mutable access still permits replacement or swapping of its contents. Address-sensitive structures need an additional contract, and Rust 1.33 stabilizes Pin to express it.
The February 28 release announcement introduces std::pin::Pin<P> and the Unpin marker trait. It describes pinning as important groundwork for asynchronous programming, while making clear that stable async and await support has not arrived with this release. Pinning is a library mechanism with immediate uses beyond that future language work.
I find Rust compelling where systems code needs to make memory assumptions explicit. Pinning addresses one that ordinary ownership does not capture, the requirement that a particular value remain at a stable address. The useful part is the contract around access to that value, rather than merely choosing where to allocate it.
Moving a pointer and moving its value are separate operations
The versioned pinning documentation explains that Pin<P> wraps a pointer type. The guarantee concerns the pointer's target. A Pin<Box<T>> is an owned pointer to a pinned value, while Pin<&mut T> provides borrowed access to one.
The pointer wrapper can move without relocating the pointed-to value. This distinction is central to the self-referential example in the documentation. An owning pointer can be passed around while the object containing internal pointers remains at the address those pointers expect.
Pinning does not introduce a compiler-wide prohibition on moving a type. The documentation explicitly says Rust still considers types movable. Instead, the wrapper restricts access to particular values so safe code cannot obtain the unrestricted mutable reference needed to move them in ways that violate the contract.
For a generic target without an Unpin bound, a pinned mutable reference therefore does not expose an ordinary mutable reference freely. mem::swap requires ordinary mutable references, so the straightforward operation is unavailable through that restricted access. An unsafe operation can expose more access, but the programmer then takes responsibility for preserving pinning.
Self-reference explains why address stability matters
The documentation's self-referential structure contains a String and a raw pointer to that field. If the structure moves, the stored address can refer to the old location of the field. Ownership of the string's allocation does not repair a raw pointer to the containing field's location.
The example first creates the structure without its final internal pointer, places it in a pinned box, and only then establishes the pointer. Ordering those steps matters because the location is not stable while constructing and moving the initial value into place. Creating the pointer too early would preserve an address that construction itself could invalidate.
It uses PhantomPinned to prevent the structure from automatically receiving Unpin. The release lists that marker among the newly stable APIs. Its presence expresses that this type intends to rely on pinning, rather than automatically allowing the restrictions to be lifted.
The constructor also contains unsafe code to update the internal pointer. Pinning does not make arbitrary self-reference safe by declaration. The implementation must establish a valid internal relationship and then ensure that its public operations preserve that relationship. The wrapper gives safe callers a restricted way to use an implementation that has done that work.
Unpin means the address restriction is unnecessary
Many types do not depend on their address. The documentation names basic scalar types and references among them. Such types implement the Unpin auto trait, allowing ordinary access even when they appear behind a pinned pointer.
The trait name can be misleading if read as an action. It does not describe a procedure that relocates a pinned object. It states that the target type does not need the restrictions supplied by pinning. For an Unpin target, Pin<&mut T> can provide the corresponding ordinary mutable access safely.
This small example uses that property for an integer.
use std::pin::Pin;
fn main() {
let mut value = 7;
let pinned = Pin::new(&mut value);
*pinned.get_mut() = 9;
assert_eq!(value, 9);
}
The example does not demonstrate an address-sensitive implementation. It demonstrates why ordinary values remain convenient. A library interface can use Pin for cases that need it without forcing an integer to acquire restrictions that its semantics do not require.
The target's trait implementation is what matters. Whether the pointer type itself implements Unpin does not determine the restriction on the pointed-to value. Confusing the pointer and target here would undermine the reason for wrapping a pointer in the first place.
I appreciate that distinction because it keeps the unusual requirement local to the types that need it. A common interface can accept pinned access while ordinary address-independent values retain their ordinary operations. The complexity remains concentrated in implementations that establish and rely on the address contract.
Storage must remain valid until destruction
The documented guarantee extends beyond avoiding movement. Pinned storage cannot be deallocated, repurposed or otherwise invalidated before the value's destructor runs. A pointer that remains numerically unchanged is useless if the storage it names has been reused for another value.
An intrusive doubly linked list illustrates the requirement. Elements contain links to their neighbors, and an element's destructor repairs neighboring links when removing it. If storage disappeared without running that destructor, other elements could retain invalid pointers. The pinning contract therefore has to cover invalidation as well as relocation.
This is not a promise that every pinned value will eventually be destroyed. The documentation permits leaking a pinned allocation, for example by forgetting its owning pointer. The obligation is that its storage not be freed or reused without the required destruction. Leaking storage and invalidating storage have different effects on pointers into it.
Destructors themselves require attention because Drop::drop receives &mut self. A type relying on pinning must not use that access to move pinned data out. The documentation explains that the destructor has to behave as though its access were pinned, even though the trait signature is not written that way.
Access to fields can create further obligations
Pinning a structure does not automatically settle which operations should be exposed for each field. A projection that produces a pinned pointer to a field promises that the field participates in the pinning contract. That promise restricts the containing type's other operations and destructor.
For example, a wrapper cannot offer pinned access to an address-sensitive field and later offer an ordinary operation that moves that same field out. The documentation discusses a take-like operation on an Option<T> field as an incompatible possibility. The public methods must agree about the field's lifetime and movement restrictions.
The container's Unpin behavior must agree with those promises too. If its projected fields rely on stable addresses, an unrestricted Unpin implementation on the wrapper would defeat the guarantee. This is why field projection is an implementation decision with safety consequences, rather than a harmless convenience conversion.
The versioned documentation also excludes packed structures from these pinning patterns because dropping their fields can require moving them to aligned locations. A representation choice can therefore conflict with the address guarantee even when ordinary methods appear careful. The complete contract includes the operations generated around destruction.
Custom pointer implementations introduce another obligation. The documentation explains that Pin relies on Deref and DerefMut implementations not moving out of their own self parameter and returning access to the pinned data correctly. A pointer abstraction with unusual dereference behavior cannot be assumed suitable merely because it has the required trait signatures.
This follows from the mechanism's location in the library. The wrapper controls the operations it exposes, but an underlying pointer implementation must honor the assumptions used by those operations. Address stability is a property maintained by the cooperating implementation, with safe access restricted so callers cannot violate it through the provided interface.
Stable groundwork with a limited scope
The release announcement expects pinning to be used mainly by library authors. Most callers should encounter the resulting APIs without writing their own unsafe address-sensitive implementation. Understanding the contract still helps distinguish an owned allocation from a value whose address is guaranteed to remain suitable for internal pointers.
Rust 1.33 supplies the stable types and marker traits for that distinction. An implementation must establish address-sensitive invariants, restrict the access it exposes and preserve storage through destruction. Safe callers can then use the pinned interface without being able to perform the ordinary move operations that would invalidate those invariants.