在 Rust 中,所有的类型可以分为两类:
类型的值可以在内存中安全地被移动,例如数值、字符串、布尔值、结构体、枚举,总之你能想到的几乎所有类型都可以落入到此范畴内 自引用类型
Pin
Pin它可以防止一个类型在内存中被移动. Pin 不按套路出牌,它是一个结构体
#![allow(unused)] fn main() { pub struct Pin<P> { pointer: P, } }
Unpin
事实上,绝大多数类型都不在意是否被移动(开篇提到的第一种类型),因此它们都自动实现了 Unpin 特征。
Unpin 是一个特征,它表明一个类型可以随意被移动,那么问题来了,可以被 Pin 住的值,它有没有实现什么特征呢? 答案很出乎意料,可以被 Pin 住的值实现的特征是 !Unpin ,大家可能之前没有见过,但是它其实很简单,! 代表没有实现某个特征的意思,!Unpin 说明类型没有实现 Unpin 特征,那自然就可以被 Pin 了。
那是不是意味着类型如果实现了 Unpin 特征,就不能被 Pin 了?其实,还是可以 Pin 的,毕竟它只是一个结构体,你可以随意使用,但是不再有任何效果而已,该值一样可以被移动!
相信大家看到这里,脑袋里已经快被 Pin 、 Unpin 、 !Unpin 整爆炸了,没事,我们再来火上浇油下:)
- 若 T: Unpin ( Rust 类型的默认实现),那么 Pin<'a, T> 跟 &'a mut T 完全相同,也就是 Pin 将没有任何效果, 该移动还是照常移动
- 绝大多数标准库类型都实现了 Unpin ,事实上,对于 Rust 中你能遇到的绝大多数类型,该结论依然成立 ,其中一个例外就是:async/await 生成的 Future 没有实现 Unpin
- 你可以通过以下方法为自己的类型添加 !Unpin 约束:
- 使用文中提到的 std::marker::PhantomPinned
- 使用nightly 版本下的 feature flag
- 可以将值固定到栈上,也可以固定到堆上
- 将 !Unpin 值固定到栈上需要使用 unsafe
- 将 !Unpin 值固定到堆上无需 unsafe ,可以通过 Box::pin 来简单的实现
- 当固定类型T: !Unpin时,你需要保证数据从被固定到被 drop 这段时期内,其内存不会变得非法或者被重用