Как звучит `pin_project` без` Unpin` - PullRequest
0 голосов
/ 01 августа 2020

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_a и field_b закреплены неявно?

1 Ответ

2 голосов
/ 02 августа 2020

Как описано в документации по проекции штифта , гарантия, которую делает 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);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...