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, {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., /// For information on what "pinning" is, see the [`pin` module] documentation.
/// through assignment or [`mem::replace`]) to always be safe.
/// ///
/// The [`Pin`][Pin] type is used instead to prevent moves through the type /// Implementing the `Unpin` trait for `T` expresses the fact that `T` is pinning-agnostic:
/// system. Pointers `P<T>` wrapped in the [`Pin<P<T>>`][Pin] wrapper can't be /// it shall not expose nor rely on any pinning guarantees. This, in turn, means that a
/// moved out of. See the [`pin` module] documentation for more information on /// `Pin`-wrapped pointer to such a type can feature a *fully unrestricted* API.
/// pinning. /// 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 idea of this trait is to alleviate the reduced ergonomics of APIs that require the use
/// the type, which then allows moving `T` out of [`Pin<P<T>>`][Pin] with /// of [`Pin`] for soundness for some types, but which also want to be used by other types that
/// functions such as [`mem::replace`]. /// 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, /// For more discussion on the consequences of [`Unpin`] within the wider scope of the pinning
/// [`mem::replace`] happily moves `!Unpin` data (it works for any `&mut T`, not /// system, see the [section about `Unpin`] in the [`pin` module].
/// 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 /// `Unpin` has no consequence at all for non-pinned data. In particular, [`mem::replace`] happily
/// need for that, and *that* is what makes this system work. /// 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`: /// 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()); /// 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 /// [`mem::replace`]: crate::mem::replace "mem replace"
/// [Pin]: crate::pin::Pin /// [`Future`]: crate::future::Future "Future"
/// [`pin` module]: crate::pin /// [`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")] #[stable(feature = "pin", since = "1.33.0")]
#[diagnostic::on_unimplemented( #[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", 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); 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 --> $SRC_DIR/core/src/pin.rs:LL:COL
error: aborting due to 1 previous error 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 --> $DIR/coerce-unsafe-closure-to-unsafe-fn-ptr.rs:2:31
| |
LL | let _: unsafe fn() = || { ::std::pin::Pin::new_unchecked(&0_u8); }; 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 | 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 --> $SRC_DIR/core/src/pin.rs:LL:COL
help: consider borrowing here help: consider borrowing here
| |

View File

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