tezvyn:

Rust's Pin: Fixing a Value's Memory Address

AI-drafted, machine-checkedSource: doc.rust-lang.orgadvanced

Pin<P> tells the Rust compiler a value must not move from its memory location. Think of it as nailing an object to a specific spot on the memory shelf. This is crucial for self-referential types, like those in async runtimes.

WHY IT EXISTS By default, Rust values are freely movable; the compiler can copy their bytes to a new memory location during an assignment or function call. This is usually efficient and safe. However, it's a disaster for self-referential types—structs that contain pointers to their own data. If such a struct moves, its internal pointers become invalid, pointing to deallocated memory. Pin was created to prevent this.

THE MENTAL MODEL Think of Pin as nailing a value to a pinboard in memory. Once pinned, the value cannot be moved from that location for the rest of its lifetime. This guarantee allows other, typically unsafe, code to hold pointers to that value and trust that they will remain valid. Pin doesn't change the data; it changes the rules about what you're allowed to do with it.

HOW IT WORKS Pin is a wrapper type, Pin, where P is a pointer like Box<T> or &mut T. It restricts the API to prevent operations that could move the underlying value T. The key is that Pin prevents you from getting a &mut T to the data. Without a &mut T, safe code cannot perform a move operation (like std::mem::swap). Most types implement the Unpin marker trait, which signals that they don't care about being moved and opts them out of this protection. For self-referential types, you must ensure they do not implement Unpin.

WHEN TO USE IT Pinning is essential when implementing or using self-referential data structures. The most common encounter for developers is with async/await. The Futures generated by async blocks often hold state that refers to itself across await points. To safely poll such a Future, it must be pinned, guaranteeing its state's location doesn't change while it's paused.

WHEN NOT TO USE IT Do not use Pin for ordinary data. Most types are Unpin and gain no benefit from the added complexity. Pin is a specialized tool for managing memory-address-sensitive types. Using it unnecessarily complicates your code and restricts how values can be used, making APIs more difficult to work with.

ONE CANONICAL EXAMPLE A self-referential struct is the classic case. Imagine a struct that contains both a String and a raw pointer to a character within that String. If an instance of this struct is moved, the String's buffer is reallocated, but the raw pointer inside the struct still points to the old, now-invalid location. By creating this struct within a Pin<Box<T>>, we guarantee it will not move, keeping the internal pointer valid.

Read the original → doc.rust-lang.org

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.