Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why pin-project implement Unpin for any struct when no structural fields?

Tags:

rust

use std::marker::PhantomPinned;

use pin_project::pin_project;

#[pin_project]
struct Struct<T> {
    field: T,
    #[pin] // <------ This `#[pin]` is required to make `Struct` to `!Unpin`.
    _pin: PhantomPinned,
}

Note that using PhantomPinned without #[pin] attribute has no effect.

Rust auto-implements Unpin when all struct fields implement Unpin, but with #[pin_project]'s macro, if no #[pin] field, the struct always implements Unpin, event if it has !Unpin fields.

Why is pin-project designed this way?

like image 429
bilosikia Avatar asked Dec 19 '25 18:12

bilosikia


1 Answers

The #[pin_project] attribute is designed for structs and enums where at least one of the fields is !Unpin and provides a safe interface for accessing both Unpin and !Unpin fields. By default, no fields are considered structurally pinned by #[pin_project]. If you do not add a #[pin] then the macro will generate access to &mut Field from Pin<&mut Struct>.

So even if a field is !Unpin, by omitting #[pin], you are creating a way to access an unprotected &mut Field. Therefore, you are not treating it as pinned by Pin<&mut Struct> and thus your struct can still be Unpin.

Why pin-project implement Unpin for any struct when no structural fields?

Directly from the Rust std::pin module (emphasis mine): "The struct must only be Unpin if all the structural fields are Unpin." And by omitting #[pin], you are communicating to the macro that the field is not structural (unlike how the compiler considers all fields structural when auto-implementing Unpin).

Why is pin-project designed this way?

For one, a macro does not know the properties of the fields it sees, it only sees the AST for the struct or enum that it is attached to. So it cannot know that _pin is !Unpin or not and act accordingly.

With that in mind, it makes more sense for the structural pinning behavior to be opt-in rather than opt-out. Almost all Rust types are Unpin, and if you were to try to use a &mut Field that is !Unpin as a Pin<&mut Field> (as is common when implementing Future or third-party Async* trait) then you would have to use unsafe to do so and negates the benefits of using the macro.

like image 92
kmdreko Avatar answered Dec 21 '25 17:12

kmdreko