pin_project allows struct designers to get pinned references to struct fields out of a pinned reference to the whole struct. It further allows the designer to specify which fields need pinned references and which can simply get a regular reference:
#[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;
}
}
My question is how can the field_b part be sound without B: Unpin? The creation of a pinned reference "pins" the struct, mandating that the referenced struct must not move out of its location in memory. Wouldn't this property apply recursively, in the same way that mutability does? If Demo is pinned, doesn't that mean that field_a and field_b are implicitly pinned?
As described in the documentation for pin projection, the guarantee that
Pinmakes is not necessarily recursive.The primary guarantee here is that "
Pin<P>prevents certain values (pointed to by pointers wrapped inPin<P>) from being moved." By design, this applies specifically to those values as a whole, withPinitself making no claims about the fields of those values.Pincould have been designed differently, but this particular approach is quite useful, because it lets each particular use case decide for itself whether it needs "structural" pinning (where a field must be pinned on its own) and "non-structural" pinning (where you can safely move or swap a field).As an example, imagine a value of type
PinMethat must be pinned, and put this value into a structWrapper. Pointers to suchWrappervalues must bePinpointers, to prevent moving the innerPinMe:But if
Wrapperhas another field, completely unrelated toPinMe, there is no reason not to move or swap that field:The choice of structural vs non-structural pinning depends entirely on the invariants you need to uphold. If
field_bneeds to remain right next tofield_a, then add#[pin]to it. But if that isn't something yourDemotype needs, you can leave it out, which provides fewer guarantees but is still safe.Edit: Further, this applies even if that extra field does not implement
Unpin, so long as nothing has constructed aPinpointing directly to it. For example: