From 06b2affa7879e609bcef70d726f900d8430f6e0a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 20 Feb 2019 18:28:12 +0100 Subject: [PATCH] tweak pinning projections --- src/libcore/pin.rs | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/libcore/pin.rs b/src/libcore/pin.rs index 6d3c3af17f5..01d384f3e19 100644 --- a/src/libcore/pin.rs +++ b/src/libcore/pin.rs @@ -174,29 +174,31 @@ //! 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<&[mut] Struct>) -> Pin<&[mut] Field>`? -//! In a similar vein, when can a container type (such as `Vec`, `Box`, or `RefCell`) -//! have an operation with type `fn(Pin<&[mut] Container>) -> Pin<&[mut] T>`? +//! In a similar vein, when can a generic wrapper type (such as `Vec`, `Box`, or `RefCell`) +//! have an operation with type `fn(Pin<&[mut] Wrapper>) -> Pin<&[mut] T>`? //! //! This question is closely related to the question of whether pinning is "structural": //! when you have pinned a wrapper type, have you pinned its contents? Deciding this -//! is entirely up to the author of any given type. However, adding a -//! projection to the API answers that question with a "yes" by offering pinned access -//! to the contents. In that case, there are a couple requirements to be upheld: +//! is entirely up to the author of any given type. For many types, both answers are reasonable +//! (e.g., there could be a version of `Vec` with structural pinning and another +//! version where the contents remain movable even when the `Vec` is pinned). +//! If pinning is not structural, the wrapper can `impl Unpin for Wrapper`. +//! If pinning is structural, the wrapper type can offer pinning projections. +//! However, structural pinning comes with a few extra requirements: //! //! 1. The wrapper must only be [`Unpin`] if all the fields one can project to 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 Container`. (Notice that adding a projection operation +//! `impl Unpin for Wrapper`. (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 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. //! You have to guarantee that you do not move a field inside your `Drop` implementation. -//! 3. Your wrapper type must *not* be `#[repr(packed)]`. Packed structs have their fields -//! moved around when they are dropped to properly align them, which is in conflict with -//! claiming that the fields are pinned when your struct is. -//! 4. You must make sure that you uphold the [`Drop` guarantee][drop-guarantee]: +//! In particular, as explained previously, this means that your wrapper type must *not* +//! be `#[repr(packed)]`. +//! 3. You must make sure that you uphold the [`Drop` guarantee][drop-guarantee]: //! once your wrapper 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 @@ -204,7 +206,7 @@ //! `Drop` guarantee, because it can lead to elements being deallocated without //! their destructor being called. (`VecDeque` has no pinning projections, so this //! does not cause unsoundness.) -//! 5. You must not offer any other operations that could lead to data being moved out of +//! 4. You must not offer any other operations that could lead to data being moved out of //! the fields when your type is pinned. This is usually not a concern, but can become //! tricky when interior mutability is involved. For example, imagine if `RefCell` //! had a method `fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T>`. @@ -222,13 +224,20 @@ //! reference we got later. //! //! On the other hand, if you decide *not* to offer any pinning projections, you -//! are free to `impl Unpin for Container`. In the standard library, +//! do not have to do anything. If your type also does not do any pinning itself, +//! you are free to `impl Unpin for Wrapper`. In the standard library, //! this is done for all pointer types: `Box: Unpin` holds for all `T`. //! It makes sense to do this for pointer types, because moving the `Box` //! does not actually move the `T`: the `Box` can be freely movable even if the `T` //! is not. In fact, even `Pin>` and `Pin<&mut T>` are always `Unpin` themselves, //! for the same reason. //! +//! Another case where you might want to have a wrapper without structural pinning is when even +//! a pinned wrapper lets its contents move, e.g. with a `take`-like operation. And, finally, +//! if it is not possible to satisfy the requirements for structural pinning, it makes sense +//! to add the `impl Unpin for Wrapper` to explicitly document this fact, and to let +//! library clients benefit from the easier interaction with [`Pin`] that [`Unpin`] types enjoy. +//! //! [`Pin`]: struct.Pin.html //! [`Unpin`]: ../../std/marker/trait.Unpin.html //! [`Deref`]: ../../std/ops/trait.Deref.html -- 2.44.0