explain better that structural pinning is a per-field choice
This commit is contained in:
parent
fc550d4295
commit
b66dcb98af
@ -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<T>`, `Box<T>`,
|
||||
//! or `RefCell<T>`) have an operation with type `fn(Pin<&Wrapper<T>>) ->
|
||||
//! 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<T>`, `Box<T>`, or `RefCell<T>`.
|
||||
//! 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<T> Unpin for Wrapper<T>`. (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<T> Unpin for Struct<T>`. (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<T>`: the destructor of `VecDeque<T>` 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<T>` and there is a `take`-like operation with type
|
||||
//! `fn(Pin<&mut Wrapper<T>>) -> Option<T>`,
|
||||
//! that operation can be used to move a `T` out of a pinned `Wrapper<T>` -- which means
|
||||
//! pinning cannot be structural.
|
||||
//! `fn(Pin<&mut Struct<T>>) -> Option<T>`,
|
||||
//! that operation can be used to move a `T` out of a pinned `Struct<T>` -- 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<T>`
|
||||
//! 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<T>`, both possibilites (structural pinning or not) make sense,
|
||||
//! and the choice is up to the author. A `Vec<T>` with structural pinning could
|
||||
//! have `get_pin`/`get_pin_mut` projections. However, it could *not* allow calling
|
||||
//! ## Examples
|
||||
//!
|
||||
//! For a type like `Vec<T>`, both possibilites (structural pinning or not) make sense.
|
||||
//! A `Vec<T>` 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<T>` because that would move the (structurally pinned) contents!
|
||||
//! Nor could it allow `push`, which might reallocate and thus also move the contents.
|
||||
//! A `Vec<T>` without structural pinning could `impl<T> Unpin for Vec<T>`, because the contents
|
||||
//! are never pinned and the `Vec<T>` 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<T>: 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<P>`]: struct.Pin.html
|
||||
//! [`Unpin`]: ../../std/marker/trait.Unpin.html
|
||||
//! [`Deref`]: ../../std/ops/trait.Deref.html
|
||||
|
Loading…
Reference in New Issue
Block a user