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:
commit
a9b6908e7f
@ -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
@ -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
|
||||
|
@ -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); };
|
||||
|
@ -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
|
||||
|
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user