没有 Unpin 的情况下,pin_project 的声音如何?

How is `pin_project` sound without `Unpin`

pin_project 允许结构设计人员从对整个结构的固定引用中获取对结构字段的固定引用。它还允许设计者指定哪些字段需要固定引用,哪些可以简单地获得常规引用:

#[pin_project]
struct Demo<A, B> {
    #[pin]
    field_a: A,
    field_b: B,
}

impl<A, B> Demo {
    fn show_references(self: Pin<&mut Self>) {
        let this = self.project();
        let field_a: Pin<&mut A> = this.field_a;
        let field_b: &mut B = this.field_b;
    }
}

我的问题是 field_b 部分没有 B: Unpin 怎么会是声音?固定引用的创建“固定”了结构,强制引用的结构不得移出其在内存中的位置。这个 属性 不会像可变性一样递归应用吗?如果 Demo 被固定,这是否意味着 field_afield_b 被隐式固定?

the documentation for pin projection 中所述,Pin 做出的保证不一定是递归的。

这里的主要保证是“Pin<P> 防止移动某些值(由 Pin<P> 中包含的指针指向)。”根据设计,这 特别地 应用于整个这些值,Pin 本身不对这些值的字段提出声明。

Pin 本来可以设计不同,但这种特殊方法非常有用,因为它让每个特定用例自行决定是否需要“结构化”固定(字段必须固定在其上自己的)和“非结构性”固定(您可以安全地移动或交换字段)。

举个例子,想象一个必须固定的类型 PinMe 的值,并将这个值放入结构 Wrapper 中。指向此类 Wrapper 值的指针必须是 Pin 指针,以防止移动内部 PinMe:

#[pin_project]
struct Wrapper {
    #[pin]
    pinned: PinMe,
}

fn f(w: Pin<&mut Wrapper>) {
    // We cannot move the `Wrapper` that `w` points to, as that would move `w.pinned`.
    // All we can do is grab a pinned pointer to the `PinMe`:
    let inner: Pin<&mut PinMe> = w.project().pinned;
}

但如果 Wrapper 有另一个字段,与 PinMe 完全无关,没有理由不移动或交换该字段:

#[pin_project]
struct Wrapper {
    #[pin]
    pinned: PinMe,
    counter: i32,
}

fn f(w: Pin<&mut Wrapper>) {
    let w = w.project();
    let inner: Pin<&mut PinMe> = w.pinned;
    let counter: &mut i32 = w.counter;

    // These sorts of operations do not affect the `PinMe`:
    *counter += 3;
    mem::replace(counter, 5);
}

结构固定与非结构固定的选择完全取决于您需要坚持的不变量。如果 field_b 需要保持在 field_a 旁边,则向其添加 #[pin]。但如果这不是您的 Demo 类型需要的东西,您可以将其省略,这样提供的保证较少但仍然安全。

编辑: 此外,即使该额外字段未实现 Unpin,只要没有构建直接指向的 Pin,这也适用它。例如:

#[pin_project]
struct Wrapper<T> {
    #[pin]
    pinned: PinMe,

    // We know nothing about this type except that it is `Sized`.
    // Thus, we cannot assume it implements `Unpin`.
    counter: T,
}

fn f<T>(w: Pin<&mut Wrapper<T>>, other: T) {
    let w = w.project();
    let inner: Pin<&mut PinMe> = w.pinned;
    let counter: &mut T = w.counter;

    // This is still okay:
    mem::replace(counter, other);
}