Rust中为什么!Unpin类型用Box::pin而Unpin类型用Pin::new?
Rust Pin 构造规则疑问解答
你的理解存在的核心错误
你的三条推理逻辑完全是颠倒的,修正如下:
- 第一点:Pin 确实是为了支撑
!Unpin类型的安全使用(比如自引用结构)而设计,但它同时完全兼容Unpin类型,并非只能用于!Unpin类型。 - 第二点:
Pin::new是专门给Unpin类型设计的安全构造方法,不能接收!Unpin类型的指针。如果你要为!Unpin类型创建 Pin 实例,必须使用Pin::new_unchecked,且需要手动遵守 Pin 的安全契约。 - 第三点:
Unpin类型不仅可以用Pin::new创建 Pin 实例,而且因为它本身移动安全,就算被 Pin 包裹,也依然可以安全获取可变引用进行移动操作,Pin 对Unpin类型不会额外增加移动限制。
背后的设计逻辑
我们可以从 Pin::new 的签名入手理解:
pub fn new(pointer: P) -> Pin<P> where P: Deref, <P as Deref>::Target: Unpin,
Rust 标准库的安全函数设计原则是:所有安全函数必须保证无论调用者怎么用,都不会触发未定义行为。
对于 Unpin 类型来说,它本身就表示「移动这个类型是完全安全的」,哪怕你从 Pin 包裹的指针里拿到了可变引用,就算用 std::mem::swap 移动它也不会出问题。因此这种场景下编译器可以自动保证安全,所以开放了安全的 Pin::new 构造方法。
而对于 !Unpin 类型,它本身是移动不安全的(比如存在自引用指针),要把它放进 Pin 里,你必须手动保证:从构造 Pin 开始,直到该对象被销毁,它的内存地址永远不会发生变化,不会被移动。这个约定编译器没有办法自动检查验证,所以必须用标记为 unsafe 的 Pin::new_unchecked 来构造,把安全责任交给开发者自己承担。
如果允许用户用安全的 Pin::new 随便构造 !Unpin 类型的 Pin 实例,开发者很可能在不知情的情况下移动了对象,触发未定义行为,这就是为什么编译器会直接禁止你这么做的原因。
内容的提问来源于stack exchange,提问作者calvin
相关产品推荐
相关产品推荐

