Rollup merge of #116129 - fu5ha:better-pin-docs-2, r=Amanieu

Rewrite `pin` module documentation to clarify usage and invariants

The documentation of `pin` today does not give a complete treatment of pinning from first principles, nor does it adequately help build intuition and understanding for how the different elements of the pinning story fit together.

This rewrite attempts to address these in a way that makes the concept more approachable while also making the documentation more normative.

This PR picks up where `@mcy` left off in #88500 (thanks to him for the original work and `@Manishearth` for mentioning it such that I originally found it). I've directly incorporated much of the feedback left on the original PR and have rewritten and changed some of the main conceits of the prose to better adhere to the feedback from the reviewers on that PR or just explain something in (hopefully) a better way.
This commit is contained in:
Matthias Krüger 2024-01-08 00:38:33 +01:00 committed by GitHub
commit a9b6908e7f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 1086 additions and 357 deletions

View File

@ -899,25 +899,37 @@ marker_impls! {
{T: ?Sized} &mut T,
}
/// Types that can be safely moved after being pinned.
/// Types that do not require any pinning guarantees.
///
/// Rust itself has no notion of immovable types, and considers moves (e.g.,
/// through assignment or [`mem::replace`]) to always be safe.
/// For information on what "pinning" is, see the [`pin` module] documentation.
///
/// The [`Pin`][Pin] type is used instead to prevent moves through the type
/// system. Pointers `P<T>` wrapped in the [`Pin<P<T>>`][Pin] wrapper can't be
/// moved out of. See the [`pin` module] documentation for more information on
/// pinning.
/// Implementing the `Unpin` trait for `T` expresses the fact that `T` is pinning-agnostic:
/// it shall not expose nor rely on any pinning guarantees. This, in turn, means that a
/// `Pin`-wrapped pointer to such a type can feature a *fully unrestricted* API.
/// In other words, if `T: Unpin`, a value of type `T` will *not* be bound by the invariants
/// which pinning otherwise offers, even when "pinned" by a [`Pin<Ptr>`] pointing at it.
/// When a value of type `T` is pointed at by a [`Pin<Ptr>`], [`Pin`] will not restrict access
/// to the pointee value like it normally would, thus allowing the user to do anything that they
/// normally could with a non-[`Pin`]-wrapped `Ptr` to that value.
///
/// Implementing the `Unpin` trait for `T` lifts the restrictions of pinning off
/// the type, which then allows moving `T` out of [`Pin<P<T>>`][Pin] with
/// functions such as [`mem::replace`].
/// The idea of this trait is to alleviate the reduced ergonomics of APIs that require the use
/// of [`Pin`] for soundness for some types, but which also want to be used by other types that
/// don't care about pinning. The prime example of such an API is [`Future::poll`]. There are many
/// [`Future`] types that don't care about pinning. These futures can implement `Unpin` and
/// therefore get around the pinning related restrictions in the API, while still allowing the
/// subset of [`Future`]s which *do* require pinning to be implemented soundly.
///
/// `Unpin` has no consequence at all for non-pinned data. In particular,
/// [`mem::replace`] happily moves `!Unpin` data (it works for any `&mut T`, not
/// just when `T: Unpin`). However, you cannot use [`mem::replace`] on data
/// wrapped inside a [`Pin<P<T>>`][Pin] because you cannot get the `&mut T` you
/// need for that, and *that* is what makes this system work.
/// For more discussion on the consequences of [`Unpin`] within the wider scope of the pinning
/// system, see the [section about `Unpin`] in the [`pin` module].
///
/// `Unpin` has no consequence at all for non-pinned data. In particular, [`mem::replace`] happily
/// moves `!Unpin` data, which would be immovable when pinned ([`mem::replace`] works for any
/// `&mut T`, not just when `T: Unpin`).
///
/// *However*, you cannot use [`mem::replace`] on `!Unpin` data which is *pinned* by being wrapped
/// inside a [`Pin<Ptr>`] pointing at it. This is because you cannot (safely) use a
/// [`Pin<Ptr>`] to get an `&mut T` to its pointee value, which you would need to call
/// [`mem::replace`], and *that* is what makes this system work.
///
/// So this, for example, can only be done on types implementing `Unpin`:
///
@ -935,11 +947,22 @@ marker_impls! {
/// mem::replace(&mut *pinned_string, "other".to_string());
/// ```
///
/// This trait is automatically implemented for almost every type.
/// This trait is automatically implemented for almost every type. The compiler is free
/// to take the conservative stance of marking types as [`Unpin`] so long as all of the types that
/// compose its fields are also [`Unpin`]. This is because if a type implements [`Unpin`], then it
/// is unsound for that type's implementation to rely on pinning-related guarantees for soundness,
/// *even* when viewed through a "pinning" pointer! It is the responsibility of the implementor of
/// a type that relies upon pinning for soundness to ensure that type is *not* marked as [`Unpin`]
/// by adding [`PhantomPinned`] field. For more details, see the [`pin` module] docs.
///
/// [`mem::replace`]: crate::mem::replace
/// [Pin]: crate::pin::Pin
/// [`pin` module]: crate::pin
/// [`mem::replace`]: crate::mem::replace "mem replace"
/// [`Future`]: crate::future::Future "Future"
/// [`Future::poll`]: crate::future::Future::poll "Future poll"
/// [`Pin`]: crate::pin::Pin "Pin"
/// [`Pin<Ptr>`]: crate::pin::Pin "Pin"
/// [`pin` module]: crate::pin "pin module"
/// [section about `Unpin`]: crate::pin#unpin "pin module docs about unpin"
/// [`unsafe`]: ../../std/keyword.unsafe.html "keyword unsafe"
#[stable(feature = "pin", since = "1.33.0")]
#[diagnostic::on_unimplemented(
note = "consider using the `pin!` macro\nconsider using `Box::pin` if you need to access the pinned value outside of the current scope",

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,7 @@ note: required because it appears within the type `Sleep`
|
LL | struct Sleep(std::marker::PhantomPinned);
| ^^^^^
note: required by a bound in `Pin::<P>::new`
note: required by a bound in `Pin::<Ptr>::new`
--> $SRC_DIR/core/src/pin.rs:LL:COL
error: aborting due to 1 previous error

View File

@ -1,4 +1,4 @@
error[E0133]: call to unsafe function `Pin::<P>::new_unchecked` is unsafe and requires unsafe function or block
error[E0133]: call to unsafe function `Pin::<Ptr>::new_unchecked` is unsafe and requires unsafe function or block
--> $DIR/coerce-unsafe-closure-to-unsafe-fn-ptr.rs:2:31
|
LL | let _: unsafe fn() = || { ::std::pin::Pin::new_unchecked(&0_u8); };

View File

@ -6,7 +6,7 @@ LL | Pin::new(S).x();
| |
| required by a bound introduced by this call
|
note: required by a bound in `Pin::<P>::new`
note: required by a bound in `Pin::<Ptr>::new`
--> $SRC_DIR/core/src/pin.rs:LL:COL
help: consider borrowing here
|

View File

@ -52,7 +52,7 @@ LL | Pin::new(x)
|
= note: consider using the `pin!` macro
consider using `Box::pin` if you need to access the pinned value outside of the current scope
note: required by a bound in `Pin::<P>::new`
note: required by a bound in `Pin::<Ptr>::new`
--> $SRC_DIR/core/src/pin.rs:LL:COL
error[E0277]: `dyn Future<Output = i32> + Send` cannot be unpinned
@ -65,7 +65,7 @@ LL | Pin::new(Box::new(x))
|
= note: consider using the `pin!` macro
consider using `Box::pin` if you need to access the pinned value outside of the current scope
note: required by a bound in `Pin::<P>::new`
note: required by a bound in `Pin::<Ptr>::new`
--> $SRC_DIR/core/src/pin.rs:LL:COL
error[E0308]: mismatched types