rust - 为什么 Futures 在 Rust 中使用引脚?

标签 rust

我理解pin是用来将数据固定到一个内存中的。当我在 Future 特征中使用 poll() 方法时,它会被连续调用,直到它返回 Poll::Ready。是否使用 pin 来确保在调用 poll() 时将数据放置在同一内存中?换句话说,它是否用于防止编译器在调用 poll 时移动可能有内存移动的代码(产生编译错误)?

最佳答案

没有。编译器永远不会在你背后移动数据。 <强> Pin不是语言保证,而是图书馆保证。

有两种方法可以构造一个Pin :

  • 不安全。
  • 如果数据是Unpin .

这确保了不安全的代码可以依赖固定保证。经验法则是:Unsafe code can never trust foreign safe code. It can only trust known safe code (such as std, or code inside its crate), or (even) foreign unsafe code .这是因为如果不安全代码依赖于外部安全代码的保证,则可能导致安全代码的 UB。一个例子(在链接的 nomicon 中引入)是 BTreeMapOrd . BTreeMap要求 item 具有全序,但其不安全代码不能依赖于此并且即使在非全序存在的情况下也必须表现良好。这是因为 Ord实现起来是安全的,因此可以使用不遵守总排序规则的安全代码以及 BTreeMap 来实现它仅使用安全代码导致未定义的行为。如果类型是已知的,而不是外来的(例如我们知道正确实现了 i32Ord),或者 BTreeMap需要 unsafe trait UnsafeOrd而不是 Ord我们可以依靠它,因为违反了 UnsafeOrd 的契约(Contract)未定义行为作为 unsafe 的特征实现。

假设我们是一个 self 参照的 future 。我们必须确保我们留在内存中的同一个位置,否则我们的自引用将悬空。因为悬挂引用是 UB,所以这必须包括不安全的代码。我们可以制作poll() unsafe fn ,但这很不方便——这意味着轮询 future 是不安全的。相反,我们需要 Pin<&mut Self> .

现在记住有两种方法可以构造一个Pin .如果我们是Unpin ,这意味着我们不是 self 参照的——也就是说,可以安全地移动——因此我们可以构造 Pin安全地。另一方面,如果我们是 self 参照的,我们就不应该是Unpin。 .现在,构建 Pin 的唯一方法使用不安全的方法 new_unchecked() ,其安全先决条件要求固定数据永远不会被移动。因为这个方法是不安全的,所以需要不安全的代码才能使用它,所以我们可以依赖它的保证(记住我们可以信任外国的不安全代码)。

这并不意味着new_unchecked()是构造 Pin<NonUnpin> 的唯一方法. Rust 中的一个常见模式是拥有一个底层的不安全机制,允许一切(只要它是健全的)但不验证任何东西,然后通过限制某些能力在其之上构建各种安全抽象。一个常见的例子是内部可变性:我们有 UnsafeCell 这是不安全的,只要您遵守别名规则就允许一切,我们在它之上有多个安全抽象,每个都通过一些限制来保证安全:

  • Cell 对于 Copy类型和非线程安全,以及通过限制为一组特定类型和原子操作来保证安全的原子类型。
  • RefCell 通过运行时检查保证安全,与UnsafeCell一样灵活但有运行时成本。
  • Mutex RwLock 通过阻塞保证安全。
  • OnceCell LazyCell 通过仅可写一次(对于线程安全版本,可能会阻塞)来保证安全。

相同的模式用于 Pin :我们有Pin::new_unchecked()那是 unsafe , 但多个抽象如 Box::pin() (需要装箱)或 pin!() 通过只允许本地固定来保证安全的宏(或 crate 中的稳定版本)。

关于rust - 为什么 Futures 在 Rust 中使用引脚?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72769316/

相关文章:

rust - "expected item, found let"是什么意思?

rust - 序列化结构上的子属性似乎不起作用

rust - 为什么 `|_| 1`不满足生存期要求

rust - 删除内联 mips 程序集中附加的 "break"指令

rust - "statics cannot evaluate destructors"在 Rust

regex - 如何通过 nom 解析匹配的分隔符?

regex - 突出显示 Rust 语法中匹配的尖括号

linux - 如何通过特定的网络接口(interface)发送?

rust - 防止不能将 `*self` 借用为不可变的,因为在访问结构中的不相交字段时它也被借用为可变的?

variables - `self` 之前的符号 (&) 在 Rust 中是什么意思?