取消固定单元格

作者提出了之前关于固定位置设计的一个变体,该变体更符合 Rust 的现有特性集。

先前设计中最奇特的方面是“固定字段”的概念,它支持固定投影。这与 Rust 中字段投影的正常工作方式非常不同:如果有对结构体的可变引用,可以获得其字段的可变引用。(作者知道 Niko Matsakis 最近探索了可能改变这一点的想法,但本文不会深入考虑该提案。)作者提出了一个具有类似属性的设计,而不是引入一种字段标记。

首先,固定引用应像其他引用一样支持投影。它们不支持的唯一原因是与Drop相关的不健全性,作者之前在这篇文章中讨论过。解决此问题的方法是说,只要通过的类型满足以下条件,固定引用就支持投影:

  1. 如果实现了Unpin,则使用自动特性机制实现,而不是手动编写impl
  2. 如果实现了Drop,要么实现Unpin,要么其析构函数使用fn drop(&pin mut self)签名。

只要类型满足这些要求,在安全代码中就无法违反固定保证。(本文的早期版本以不同且错误的方式陈述了这些标准,因为作者忘记了Unpin必须使用自动特性机制实现才能保持健全性。)

然而,仍然需要某种方式来支持未固定字段,这些字段是应用于整个对象的固定契约的例外。为此,该语言将引入一种新的“单元”类型UnpinCell,它“取消固定”其中的任何对象。UnpinCell的 API 可能如下所示:

pub struct UnpinCell<T>(T);

impl<T> UnpinCell<T> {
    pub fn new(value: T) -> UnpinCell<T> {
        UnpinCell(value)
    }

    pub fn into_inner(self) -> T {
        self.0
    }
}

// 即使 T:!Unpin
impl<T> Unpin for UnpinCell<T> { }

impl<T> Deref for UnpinCell<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.0
    }
}

impl<T> DerefMut for UnpinCell<T> {
    fn deref_mut(&mut self) -> &mut T {
        &mut self.0
    }
}

这个极其简单的 API 允许通过固定指针对UnpinCell内部的值进行可变访问,即使该值未实现Unpin。这是合理的,因为UnpinCell创建了一个屏障,通过该屏障无法进行固定投影,因此单元内部的对象永远不会被视为固定的。

重新审视之前文章中的MaybeDone示例,现在它将如下所示:

enum MaybeDone<F: Future> {
    Polling(F),
    Done(UnpinCell<Option<F::Output>>),
}

impl<F: Future> MaybeDone<F> {
    fn maybe_poll(&pin mut self, cx: &mut Context<'_>) {
        if let MaybeDone::Polling(fut) = self {
            if let Poll::Ready(res) = fut.poll(cx) {
                *self = MaybeDone::Done(UnpinCell::new(Some(res)));
            }
        }
    }

    fn is_done(&self) -> bool {
        matches!(self, &MaybeDone::Done(_))
    }

    fn take_output(&pin mut self) -> Option<F::Output> {
        // res: &pin mut UnpinCell<Option<F::Output>>
        if let MaybeDone::Done(res) = self {
            // 两个解引用可变强制转换将其解析为 Option::take
            res.take()
        } else {
            None
        }
    }
}

(作者已更新语法,使用pin而不是pinned,因为这是该项目的固定性实验使用的语法。)

这种组合使固定位置成为对语言的更微小更改:固定引用的行为类似于普通引用,要获取作为异常的字段,有一个类似单元的 API 用于“内部不可固定性”,就像“内部可变性”一样。在作者看来,这优于字段修饰符,因为它不会引入任何新的语言特性类别。

阅读 11
0 条评论