Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit a9b6908

Browse files
authoredJan 7, 2024
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.
2 parents 75c68cf + 7fd841c commit a9b6908

File tree

6 files changed

+1104
-375
lines changed

6 files changed

+1104
-375
lines changed
 

‎library/core/src/marker.rs

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -899,25 +899,37 @@ marker_impls! {
899899
{T: ?Sized} &mut T,
900900
}
901901

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

‎library/core/src/pin.rs

Lines changed: 1052 additions & 346 deletions
Large diffs are not rendered by default.

‎tests/ui/async-await/pin-needed-to-poll-2.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ note: required because it appears within the type `Sleep`
1313
|
1414
LL | struct Sleep(std::marker::PhantomPinned);
1515
| ^^^^^
16-
note: required by a bound in `Pin::<P>::new`
16+
note: required by a bound in `Pin::<Ptr>::new`
1717
--> $SRC_DIR/core/src/pin.rs:LL:COL
1818

1919
error: aborting due to 1 previous error

‎tests/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.stderr

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

‎tests/ui/self/arbitrary_self_types_pin_needing_borrow.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | Pin::new(S).x();
66
| |
77
| required by a bound introduced by this call
88
|
9-
note: required by a bound in `Pin::<P>::new`
9+
note: required by a bound in `Pin::<Ptr>::new`
1010
--> $SRC_DIR/core/src/pin.rs:LL:COL
1111
help: consider borrowing here
1212
|

‎tests/ui/suggestions/expected-boxed-future-isnt-pinned.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ LL | Pin::new(x)
5252
|
5353
= note: consider using the `pin!` macro
5454
consider using `Box::pin` if you need to access the pinned value outside of the current scope
55-
note: required by a bound in `Pin::<P>::new`
55+
note: required by a bound in `Pin::<Ptr>::new`
5656
--> $SRC_DIR/core/src/pin.rs:LL:COL
5757

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

7171
error[E0308]: mismatched types

0 commit comments

Comments
 (0)
Please sign in to comment.