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