From b66dcb98af7c0785ace170a074640388d24e8536 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 15 Jun 2019 23:51:42 +0200 Subject: [PATCH] explain better that structural pinning is a per-field choice --- src/libcore/pin.rs | 131 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 102 insertions(+), 29 deletions(-) diff --git a/src/libcore/pin.rs b/src/libcore/pin.rs index c5247e134c8..e6fdfb8af5f 100644 --- a/src/libcore/pin.rs +++ b/src/libcore/pin.rs @@ -166,43 +166,106 @@ //! implementation as well: if an element of your type could have been pinned, //! you must treat Drop as implicitly taking `Pin<&mut Self>`. //! -//! In particular, if your type is `#[repr(packed)]`, the compiler will automatically +//! For example, you could implement `Drop` as follows: +//! ```rust,ignore +//! impl Drop for Type { +//! fn drop(&mut self) { +//! // `new_unchecked` is okay because we know this value is never used +//! // again after being dropped. +//! inner_drop(unsafe { Pin::new_unchecked(self)}); +//! fn inner_drop(this: Pin<&mut Type>) { +//! // Actual drop code goes here. +//! } +//! } +//! } +//! ``` +//! `inner_drop` has the type that `drop` *should* have, so this makes sure that +//! you do not accidentally use `self`/`this` in a way that is in conflict with pinning. +//! +//! Moreover, if your type is `#[repr(packed)]`, the compiler will automatically //! move fields around to be able to drop them. As a consequence, you cannot use //! pinning with a `#[repr(packed)]` type. //! //! # Projections and Structural Pinning //! -//! One interesting question arises when considering the interaction of pinning -//! and the fields of a struct. When can a struct have a "pinning projection", -//! i.e., an operation with type `fn(Pin<&Struct>) -> Pin<&Field>`? In a -//! similar vein, when can a generic wrapper type (such as `Vec`, `Box`, -//! or `RefCell`) have an operation with type `fn(Pin<&Wrapper>) -> -//! Pin<&T>`? +//! When working with pinned structs, the question arises how one can access the +//! fields of that struct in a method that takes just `Pin<&mut Struct>`. +//! The usual approach is to write helper methods (so called *projections*) +//! that turn `Pin<&mut Struct>` into a reference to the field, but what +//! type should that reference have? Is it `Pin<&mut Field>` or `&mut Field`? +//! The same question arises with the fields of an enum, and also when considering +//! container/wrapper types such as `Vec`, `Box`, or `RefCell`. +//! Also, this question arises for both mutable and shared references, we just +//! use the more common case of mutable references here for illustration. //! -//! Note: For the entirety of this discussion, the same applies for mutable references as it -//! does for shared references. +//! It turns out that it is actually up to the author of the data structure +//! to decide whether the pinned projection for a particular field turns +//! `Pin<&mut Struct>` into `Pin<&mut Field>` or `&mut Field`. There are some +//! constraints though, and the most important constraint is *consistency*: +//! every field can be *either* projected to a pinned reference, *or* have +//! pinning removed as part of the projection. If both are done for the same field, +//! that will likely be unsound! //! -//! Having a pinning projection for some field means that pinning is "structural": -//! when the wrapper is pinned, the field must be considered pinned, too. -//! After all, the pinning projection lets us get a `Pin<&Field>`. +//! Basically, as the author of a data structure you get to decide for each field whether pinning +//! "propagates" to this field or not. Pinning that propagates is also called "structural", +//! because it follows the structure of the type. +//! In the following, we describe the considerations that have to be made for either choice. //! -//! However, structural pinning comes with a few extra requirements, so not all -//! wrappers can be structural and hence not all wrappers can offer pinning projections: +//! ## Pinning *is not* structural for `field` //! -//! 1. The wrapper must only be [`Unpin`] if all the structural fields are -//! `Unpin`. This is the default, but `Unpin` is a safe trait, so as the author of -//! the wrapper it is your responsibility *not* to add something like -//! `impl Unpin for Wrapper`. (Notice that adding a projection operation +//! It may seem counter-intuitive that the field of a pinned struct is not pinned, +//! but that is actually the easiest choice: if a `Pin<&mut Field>` is never created, +//! nothing can go wrong! So, if you decide that some field does not have structural pinning, +//! all you have to ensure is that you never create a pinned reference to that field. +//! +//! Then you may add a projection method that turns `Pin<&mut Struct>` into `Pin<&mut Field>`: +//! ```rust,ignore +//! impl Struct { +//! fn get_field<'a>(self: Pin<&'a mut Self>) -> &'a mut Field { +//! // This is okay because `field` is never considered pinned. +//! unsafe { &mut self.get_unchecked_mut().field } +//! } +//! } +//! ``` +//! +//! You may also make make `Struct: Unpin` *even if* the type of `field` +//! is not `Unpin`. What that type thinks about pinning is just not relevant +//! when no `Pin<&mut Field>` is ever created. +//! +//! ## Pinning *is* structural for `field` +//! +//! The other option is to decide that pinning is "structural" for `field`, +//! meaning that if the struct is pinned then so is the field. +//! +//! This allows writing a projection that creates a `Pin<&mut Field>`, thus +//! witnessing that the field is pinned: +//! ```rust,ignore +//! impl Struct { +//! fn get_field<'a>(self: Pin<&'a mut Self>) -> Pin<&'a mut Field> { +//! // This is okay because `field` is pinned when `self` is. +//! unsafe { self.map_unchecked_mut(|s| &mut s.field) } +//! } +//! } +//! ``` +//! +//! However, structural pinning comes with a few extra requirements: +//! +//! 1. The struct must only be [`Unpin`] if all the structural fields are +//! `Unpin`. This is the default, but `Unpin` is a safe trait, so it is your +//! responsibility as the author of the struct *not* to add something like +//! `impl Unpin for Struct`. (Notice that adding a projection operation //! requires unsafe code, so the fact that `Unpin` is a safe trait does not break //! the principle that you only have to worry about any of this if you use `unsafe`.) -//! 2. The destructor of the wrapper must not move structural fields out of its argument. This +//! 2. The destructor of the struct must not move structural fields out of its argument. This //! is the exact point that was raised in the [previous section][drop-impl]: `drop` takes -//! `&mut self`, but the wrapper (and hence its fields) might have been pinned before. +//! `&mut self`, but the struct (and hence its fields) might have been pinned before. //! You have to guarantee that you do not move a field inside your `Drop` implementation. -//! In particular, as explained previously, this means that your wrapper type must *not* +//! In particular, as explained previously, this means that your struct must *not* //! be `#[repr(packed)]`. +//! See that section for how to write `drop` in a way that the compiler can help you +//! not accidentally break pinning. //! 3. You must make sure that you uphold the [`Drop` guarantee][drop-guarantee]: -//! once your wrapper is pinned, the memory that contains the +//! once your struct is pinned, the memory that contains the //! content is not overwritten or deallocated without calling the content's destructors. //! This can be tricky, as witnessed by `VecDeque`: the destructor of `VecDeque` can fail //! to call `drop` on all elements if one of the destructors panics. This violates the @@ -210,11 +273,11 @@ //! their destructor being called. (`VecDeque` has no pinning projections, so this //! does not cause unsoundness.) //! 4. You must not offer any other operations that could lead to data being moved out of -//! the fields when your type is pinned. For example, if the wrapper contains an +//! the structural fields when your type is pinned. For example, if the struct contains an //! `Option` and there is a `take`-like operation with type -//! `fn(Pin<&mut Wrapper>) -> Option`, -//! that operation can be used to move a `T` out of a pinned `Wrapper` -- which means -//! pinning cannot be structural. +//! `fn(Pin<&mut Struct>) -> Option`, +//! that operation can be used to move a `T` out of a pinned `Struct` -- which means +//! pinning cannot be structural for the field holding this data. //! //! For a more complex example of moving data out of a pinned type, imagine if `RefCell` //! had a method `fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T>`. @@ -231,13 +294,16 @@ //! (using `RefCell::get_pin_mut`) and then move that content using the mutable //! reference we got later. //! -//! For a type like `Vec`, both possibilites (structural pinning or not) make sense, -//! and the choice is up to the author. A `Vec` with structural pinning could -//! have `get_pin`/`get_pin_mut` projections. However, it could *not* allow calling +//! ## Examples +//! +//! For a type like `Vec`, both possibilites (structural pinning or not) make sense. +//! A `Vec` with structural pinning could have `get_pin`/`get_pin_mut` methods to get +//! pinned references to elements. However, it could *not* allow calling //! `pop` on a pinned `Vec` because that would move the (structurally pinned) contents! //! Nor could it allow `push`, which might reallocate and thus also move the contents. //! A `Vec` without structural pinning could `impl Unpin for Vec`, because the contents //! are never pinned and the `Vec` itself is fine with being moved as well. +//! At that point pinning just has no effect on the vector at all. //! //! In the standard library, pointer types generally do not have structural pinning, //! and thus they do not offer pinning projections. This is why `Box: Unpin` holds for all `T`. @@ -249,6 +315,13 @@ //! whether the content is pinned is entirely independent of whether the pointer is //! pinned, meaning pinning is *not* structural. //! +//! When implementing a `Future` combinator, you will usually need structural pinning +//! for the nested futures, as you need to get pinned references to them to call `poll`. +//! But if your combinator contains any other data that does not need to be pinned, +//! you can make those fields not structural and hence freely access them with a +//! mutable reference even when you just have `Pin<&mut Self>` (such as in your own +//! `poll` implementation). +//! //! [`Pin

`]: struct.Pin.html //! [`Unpin`]: ../../std/marker/trait.Unpin.html //! [`Deref`]: ../../std/ops/trait.Deref.html