When is it safe to move a member value out of a pinned future?

That is what structural pinning is about:

First, I will note P<T> something like impl Deref<Target = T>, that is, some (smart) pointer type P that Deref::derefs to a T (Pin only "applies" to / make sense on such (smart) pointers).

Let's say we have:

struct Wrapper<Field> {
    field: Field,
}

Now, the question is, whether we can get a Pin<P< Field >> from a Pin<P< Wrapper<Field> >>, by "projecting" our Pin<P<_>> from the Wrapper to its field.

This already requires the basic projection P<Wrapper<Field>> -> P<Field>, which is only even possible for

  • shared references: P<T> = &T (this is not a very interesting case given that Pin<P<T>> always derefs to T)

  • unique references: P<T> = &mut T.

I will note this &[mut] T

So, the question is:

Can we go from Pin<&[mut] Wrapper<Field>> to Pin<&[mut] Field>?

The point that may still be unclear in the documentation is the following: it is up to the creator of Wrapper to decide!

So there are two possible choices for the library author, regarding each one of the struct fields:

  • either there is a structural Pin projection to that field;
    (for instance, when the ::pin_utils::unsafe_pinned! macro is used to define such projection)

    Then, for the Pin projection to be sound:

    • the whole struct must only implement Unpin when all the fields for which there is a structural Pin projection implement Unpin,

      • Thus, no implementation is allowed to use unsafe to move such fields out of a Pin<&mut Wrapper<Field>> (or Pin<&mut Self> when Self = Wrapper<Field>); for instance, Option::take() is forbidden.
    • the whole struct may only implement Drop if Drop::drop does not move any of the fields for which there is a structural projection,

    • the struct cannot be #[repr(packed)] (a corollary of the previous item).

    • In your given future::Map example, this is the case of the future field of the Map struct.

  • or there is no Pin projection to that field;

    In that case, that field is not considered pinned! (by a Pin<&mut Wrapper<Field>>)

    • thus whether Field is Unpin or not, does not matter;

      • implementations are allowed to use unsafe to move such fields out of a Pin<&mut Wrapper<Field>>; for instance, Option::take() is allowed.
    • and ::pin_utils::unsafe_unpinned! is safe to use to define a Pin<&mut Wrapper<Field>> -> &mut Field projection.

    • Drop::drop is also allowed to move such fields,

    • In your given future::Map example, this is the case of the f field of the Map struct.

1 Like