Rust 何时将成员值移出固定的未来是安全的?

Rust 何时将成员值移出固定的未来是安全的?,rust,future,Rust,Future,我正在编写一个未来的组合器,它需要使用它提供的值。对于futures 0.1,takeself:&mut self,这实际上意味着我的组合器包含一个选项,当基础未来解决时,我对它调用了选项::take 标准库中的方法取而代之的是self:Pin,因此我一直在阅读安全使用Pin所需的保证 从(pinemphasis mine)上的模块文档中: 具体地说,对于固定数据,您必须保持不变量,即从固定数据的那一刻起,直到调用drop时,其内存不会失效。内存可以通过解除分配而失效,但也可以通过将Some(v

我正在编写一个未来的组合器,它需要使用它提供的值。对于futures 0.1,take
self:&mut self
,这实际上意味着我的组合器包含一个
选项
,当基础未来解决时,我对它调用了
选项::take

标准库中的方法取而代之的是
self:Pin
,因此我一直在阅读安全使用
Pin
所需的保证

从(
pin
emphasis mine)上的模块文档中:

具体地说,对于固定数据,您必须保持不变量,即从固定数据的那一刻起,直到调用drop时,其内存不会失效。内存可以通过解除分配而失效,但也可以通过将
Some(v)
替换为
None
或调用
Vec::set_len
来“杀死”向量中的某些元素来失效

和(我的重点):

当您的类型被固定时,您不能提供可能导致数据移出字段的任何其他操作。例如,如果包装包含一个
选项
,并且有一个带类型
fn(Pin)->Option的类取操作,则该操作可用于将
T
移出固定的
包装
——这意味着固定不能是结构化的

但是,当基本未来已解决时,现有combinator对成员值调用
选项::take

fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
    match self.as_mut().future().poll(cx) {
        Poll::Pending => Poll::Pending,
        Poll::Ready(output) => {
            let f = self.f().take()
                .expect("Map must not be polled after it returned `Poll::Ready`");
            Poll::Ready(f(output))
        }
    }
}
似乎
Map
违反了
pin
文档中描述的要求,但我相信
Map
combinator的作者知道他们在做什么,并且该代码是安全的


什么逻辑允许他们以安全的方式执行此操作?

编辑:此答案不正确。它为后人保留在这里

首先让我们回顾一下为什么最初引入了
Pin
:我们希望静态地确保自引用期货不能移动,从而使其内部引用无效

记住这一点,让我们来看看<代码> map 的定义。< /P>

pub struct Map<Fut, F> {
    future: Fut,
    f: Option<F>,
}
pub结构映射{
未来:未来,
f:选择,
}
Map
有两个字段,第一个字段存储未来,第二个字段存储将未来结果映射到另一个值的闭包。我们希望支持将自引用类型直接存储在
future
中,而无需将它们放在指针后面。这意味着如果
Fut
是一个自引用类型,
Map
一旦被构造就不能被移动。这就是为什么我们必须使用
Pin
作为
Future::poll
的接收器。如果对包含自参考未来的
映射的正常可变引用曾经暴露给
未来的实现者,则用户可以通过使用
mem::replace
移动
映射来导致UB仅使用安全代码

但是,我们不需要支持在
f
中存储自引用类型。如果我们假设
映射的自参考部分
完全包含在
未来
中,我们可以自由修改
f
,只要我们不允许移动
未来

虽然自引用闭包是非常不寻常的,但是
f
可以安全移动的假设(相当于
f:Unpin
)并没有在任何地方明确说明。但是,我们仍然通过调用
take
Future::poll
中移动
f
中的值!我认为这确实是一个bug,但我不是100%确定。我认为
f()
getter应该需要
f:Unpin
,这意味着
Map
只能在闭包参数可以安全地从
Pin
后面移动时实现
Future


我很可能忽略了pinapi中的一些微妙之处,而且实现确实是安全的。我还在为它绞尽脑汁。

这都是关于结构固定的

首先,我将使用语法
p
来表示类似
impl-Deref
-一些(智能)指针类型
p
,它将
Deref::Deref
s转换为
T
<代码>Pin
仅“应用”于此类(智能)指针/在此类指针上有意义

假设我们有:

struct包装器{
字段:字段,
}
最初的问题是

通过将
Pin
Wrapper
中“投影”到其
字段,我们可以从
Pin
中获取
Pin

这需要基本投影
p->p
,这仅适用于:

  • 共享引用(
    p=&T
    )。考虑到
    Pin
    总是从s到
    T
    ,这不是一个非常有趣的例子

  • 唯一引用(
    p=&mut T

对于这种类型的投影,我将使用语法
和[mut]T

现在的问题是:

我们能从
Pin
转到
Pin

文档中可能不清楚的一点是,
Wrapper
的创建者自己决定

对于每个结构字段,库作者有两种可能的选择

有一个结构
引脚
投影到该字段 例如,宏用于定义这样的投影(
Pin->Pin

对于
引脚
投影为声音:

  • 当所有具有结构
    Pin
    投影的字段都实现
    Unpin
    时,整个结构必须只实现
    Unpin

    • 不允许任何实现使用
      safe
      将此类字段移出
      Pin
      (或
      Self=Wrapper
      时的
      Pin
      )。例如,
      pub struct Map<Fut, F> {
          future: Fut,
          f: Option<F>,
      }