It is all about structural pinning.
First, I will use the syntax P<T>
to mean something like impl Deref<Target = T>
— some (smart) pointer type P
that Deref::deref
s to a T
. Pin
only "applies" to / makes sense on such (smart) pointers.
Let's say we have:
struct Wrapper<Field> {
field: Field,
}
The initial question is
Can we get a Pin<P<Field>>
from a Pin<P<Wrapper<Field>>>
, by "projecting" our Pin<P<_>>
from the Wrapper
to its field
?
This requires the basic projection P<Wrapper<Field>> -> P<Field>
, which is only possible for:
I will use the syntax &[mut] T
for this type of projection.
The question now becomes:
Can we go from Pin<&[mut] Wrapper<Field>>
to Pin<&[mut] Field>
?
The point that may be unclear from the documentation is that it is up to the creator of Wrapper
to decide!
There are two possible choices for the library author for each struct field.
There is a structural Pin
projection to that field
For instance, the pin_utils::unsafe_pinned!
macro is used to define such a projection (Pin<&mut Wrapper<Field>> -> Pin<&mut Field>
).
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
.
- 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.
There is no structural Pin
projection to that field
For instance, the pin_utils::unsafe_unpinned!
macro is used to define such a projection (Pin<&mut Wrapper<Field>> -> &mut Field
).
In this case, that field is not considered pinned by a Pin<&mut Wrapper<Field>>
.
In your given future::Map
example, this is the case of the f
field of the Map
struct.
Example of both types of projection
impl<Fut, F> Map<Fut, F> {
unsafe_pinned!(future: Fut); // pin projection -----+
unsafe_unpinned!(f: Option<F>); // not pinned --+ |
// | |
// ... | |
// | |
fn poll (mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
// | |
match self.as_mut().future().poll(cx) { // <----+ required here
Poll::Pending => Poll::Pending, // |
Poll::Ready(output) => { // |
let f = self.f().take() // <--------+ allows this