What is `std::pin::Pin` in Rust?
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.
std::pin::Pin 是一个指针包装器,它代表了一种保证:被指向的对象不会通过该指针被移动。
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: Pinning(固定)的需求源于自引用类型。自引用结构体是地址敏感类型最常见的例子,也是引入 Pin 的主要动机。以以下结构体为例:
struct SelfRef {
data: i32,
ptr: *const i32, // points to self.data
}
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 prevent moving SelfRef once those self-references have been established.
如果我们移动该结构体的实例(例如,通过将所有权转移到另一个变量或将其返回),它的内存地址就会改变。然而,原始指针 ptr 仍然指向旧的内存位置,从而导致悬垂指针。因此,我们需要一种方法,在建立自引用后防止 SelfRef 被移动。
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.
我们最常在 async/await 和 Future 中遇到这个问题。跨越 .await 点存活的局部变量会成为编译器生成的状态机中的字段。如果对某个局部变量的引用也跨越了同一个 .await,生成的 future 就会变成自引用的。
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:
一旦开始轮询(polling),future 可能会依赖指向其自身内部其他字段的引用。在此之后移动 future 会使这些引用失效。为了防止这种情况,Future::poll 方法要求 future 必须被固定(pinned):
pub trait Future {
type Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
}
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.
通过接收 Pin<&mut Self> 而不是 &mut self,poll 的调用者必须保证 future 在开始轮询后不会被移动。
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.
Pin<P> 阻止安全代码通过该指针移动被指向的对象,同时仍然允许对被固定的值进行普通的修改。
The Problem with &mut T
&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.
如果你有一个可变引用 &mut T,像 mem::replace、mem::swap 或赋值操作等函数可以重新定位存储在该内存位置的值。
Pin restricts recovering an ordinary mutable reference. Safe code cannot recover an ordinary &mut T from a Pin<&mut T> unless T: Unpin.
Pin 限制了恢复普通可变引用的能力。除非 T: Unpin,否则安全代码无法从 Pin<&mut T> 中恢复出普通的 &mut T。
impl<'a, T: ?Sized> Pin<&'a T> {
pub const fn get_ref(self) -> &'a T { ... }
}
impl<'a, T: ?Sized> Pin<&'a mut T> {
pub const fn get_mut(self) -> &'a mut T
where
T: Unpin
{ ... }
}
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.
Pin 仅防止通过被固定的指针进行移动;它并不阻止对被固定值的修改。被固定类型上的方法可以自由修改其字段,前提是它们不移动这些字段。
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.
如果类型没有实现 Unpin(即它是 !Unpin),你无法使用安全代码获取 &mut T。你必须使用像 Pin::get_unchecked_mut 这样的不安全方法,向编译器表明你承诺不会将该值从引用中移出。
What is Unpin?
什么是 Unpin?
A type implementing Unpin does not rely on pinning for soundness.
实现 Unpin 的类型不依赖固定来保证安全性(soundness)。
// std::marker
pub 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.
Rust 中的大多数类型(如 i32、String、Vec 等)不在乎是否被移动,默认都是 Unpin 的。除非显式实现了 !Unpin,否则所有类型都会自动实现 Unpin。
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.
标记结构体 std::marker::PhantomPinned 显式地是 !Unpin 的。由于自动 trait 会自动传播,任何包含 PhantomPinned 字段的结构体也会自动成为 !Unpin。
use std::marker::PhantomPinned;
struct SelfRef {
data: i32,
ptr: *const i32,
_phantom: PhantomPinned, // makes the entire struct !Unpin
}
This is the standard way to declare that a custom struct is unsafe to move after being pinned. 这是声明自定义结构体在被固定后移动是不安全的标准方式。
Because the compiler cannot automatically detect self-references (which are typically created using unsafe raw pointers), it cannot automatically mark such structs as !Unpin.
由于编译器无法自动检测自引用(通常使用不安全的原始指针创建),它无法自动将此类结构体标记为 !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.
因此,这依赖于一种契约:开发者必须为任何自引用结构体显式地放弃 Unpin(通常通过嵌入 PhantomPinned 字段)。如果自引用类型错误地保持了 Unpin,安全代码可以从 Pin 中恢复出可变引用并移动该值,从而破坏创建自引用的不安全代码所做的假设。
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.
Pin 并不在物理上阻止值移动。相反,它是一种类型层面的保证,即该值不会通过该指针被移动。因此,安全地构建 Pin 需要确保被指向的对象在固定的生命周期内保持在稳定的内存位置。
Constructing a Pin
构建 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 本身并不固定值。相反,构建 Pin 意味着证明被指向的对象在固定的生命周期内将保持在稳定的内存位置。
Pin::new
Pin::new
The simplest way to construct a Pin is with Pin::new:
构建 Pin 最简单的方法是使用 Pin::new:
let mut value = 42;
let 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.
然而,该构造函数仅在 T: Unpin 时可用。由于 Unpin 类型不依赖固定来保证安全性,将它们包装在 Pin 中总是安全的。这种固定保证实际上是一个空操作(no-op)。
pin!
pin!
When you need to pin a value locally without allocating on the heap, you can use the pin! macro:
当你需要在本地固定一个值而无需在堆上分配内存时,可以使用 pin! 宏:
use std::pin::pin;
let future = pin!(async {
println!("Hello");
});
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.
该宏创建一个局部变量并返回一个指向它的 Pin<&mut T>。编译器确保该局部变量在其生命周期的剩余时间内不会被移动,这使得它成为在栈上固定 !Unpin 值的一种安全方式。
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.
尽管名字如此,pin! 并不固定栈内存本身。它创建了一个生命周期与局部变量绑定的固定引用。一旦变量超出作用域,固定保证即告结束。
Box::pin
Box::pin
For !Unpin types, the most common constructor is Box::pin:
对于 !Unpin 类型,最常用的构造函数是 Box::pin:
let 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.
与 pin! 创建绑定到局部变量的 Pin<&mut T> 不同,Box::pin 返回一个生命周期由 Box 拥有的 Pin<Box<T>>。由于堆分配本身不会移动,被指向的对象在 Box 的生命周期内拥有稳定的内存位置,从而可以被安全地固定。
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.
移动 Box 本身并不会移动它所拥有的值。只有存储在 Box 内部的指针被移动了;堆分配仍然保持在相同的地址。
Pin::new_unchecked
Pin::new_unchecked
Sometimes safe constructors cannot provide… 有时安全构造函数无法提供……