为什么 std::pin::Pin 这么奇怪?

主要观点:Rust 中的某些类型值需要被固定(pinned),以防止在内存中移动,通过std::pin::Pin包装类型来表达,常以接受Pin<&mut T>而不是&mut T的函数形式出现,固定会使许多“正常”编程技术变得困难并产生奇怪的副作用。
关键信息

  • 定义了一个可能因移动而无法正常工作的BagOfApples类型,通过添加PhantomPinned成员和修改initialize()方法为接受Pin<&mut Self>来实现固定,解决了类型的不安全性问题。
  • 固定的目的是在需要固定的类型可能在内存中移动时引发编译时错误,如let mut bag = BagOfApples::new(); bag.initialize();会报错,而let mut bag = Box::pin(BagOfApples::new()); bag.as_mut().initialize();则不会。
  • 对于固定类型,只有接受&mut self或拥有值的函数需要改变,共享引用&self可以始终解引用固定值。
  • 可以在值的生命周期的不同阶段进行固定,如先创建非固定值,调用相关方法后再进行固定。
  • 一旦值被固定,在安全代码中不能移除Pin包装器,但可以暂时获得常规的&mut独占引用,通过unsafe块实现,对于不需要固定的类型可以直接解引用Pin<&mut T>&mut T
  • 固定机制通过阻止获取独占引用来防止值被移动,对于自定义代码,作者需要确保移动操作不违反 Rust 编程规则。
  • 对于结构体字段中的值,默认情况下编译器不将固定类型的字段视为固定的,需要使用pin_project crate 来标记哪些字段需要在父类型固定时也被视为固定。
    重要细节
  • 示例代码中BagOfApples类型的定义及相关方法的实现,包括new()initialize()count()等方法。
  • 不同方式实现固定的示例,如使用Box::pin()Pin<Box<T>>等。
  • 对于不同类型(需要固定和不需要固定)在共享引用、独占引用和拥有值方面的处理方式。
  • 详细说明了unsafe块的使用及相关安全承诺。
  • 介绍了pin_project crate 的使用,以处理结构体中的固定字段。
  • 提醒避免依赖Unpin自动 trait 以免学习到错误的固定相关模式。
阅读 14
0 条评论