Skip to content

Commit 06b2aff

Browse files
committed
tweak pinning projections
1 parent c52560d commit 06b2aff

File tree

1 file changed

+21
-12
lines changed

1 file changed

+21
-12
lines changed

src/libcore/pin.rs

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -174,37 +174,39 @@
174174
//! One interesting question arises when considering the interaction of pinning and
175175
//! the fields of a struct. When can a struct have a "pinning projection", i.e.,
176176
//! an operation with type `fn(Pin<&[mut] Struct>) -> Pin<&[mut] Field>`?
177-
//! In a similar vein, when can a container type (such as `Vec`, `Box`, or `RefCell`)
178-
//! have an operation with type `fn(Pin<&[mut] Container<T>>) -> Pin<&[mut] T>`?
177+
//! In a similar vein, when can a generic wrapper type (such as `Vec`, `Box`, or `RefCell`)
178+
//! have an operation with type `fn(Pin<&[mut] Wrapper<T>>) -> Pin<&[mut] T>`?
179179
//!
180180
//! This question is closely related to the question of whether pinning is "structural":
181181
//! when you have pinned a wrapper type, have you pinned its contents? Deciding this
182-
//! is entirely up to the author of any given type. However, adding a
183-
//! projection to the API answers that question with a "yes" by offering pinned access
184-
//! to the contents. In that case, there are a couple requirements to be upheld:
182+
//! is entirely up to the author of any given type. For many types, both answers are reasonable
183+
//! (e.g., there could be a version of `Vec` with structural pinning and another
184+
//! version where the contents remain movable even when the `Vec` is pinned).
185+
//! If pinning is not structural, the wrapper can `impl<T> Unpin for Wrapper<T>`.
186+
//! If pinning is structural, the wrapper type can offer pinning projections.
187+
//! However, structural pinning comes with a few extra requirements:
185188
//!
186189
//! 1. The wrapper must only be [`Unpin`] if all the fields one can project to are
187190
//! `Unpin`. This is the default, but `Unpin` is a safe trait, so as the author of
188191
//! the wrapper it is your responsibility *not* to add something like
189-
//! `impl<T> Unpin for Container<T>`. (Notice that adding a projection operation
192+
//! `impl<T> Unpin for Wrapper<T>`. (Notice that adding a projection operation
190193
//! requires unsafe code, so the fact that `Unpin` is a safe trait does not break
191194
//! the principle that you only have to worry about any of this if you use `unsafe`.)
192195
//! 2. The destructor of the wrapper must not move out of its argument. This is the exact
193196
//! point that was raised in the [previous section][drop-impl]: `drop` takes `&mut self`,
194197
//! but the wrapper (and hence its fields) might have been pinned before.
195198
//! You have to guarantee that you do not move a field inside your `Drop` implementation.
196-
//! 3. Your wrapper type must *not* be `#[repr(packed)]`. Packed structs have their fields
197-
//! moved around when they are dropped to properly align them, which is in conflict with
198-
//! claiming that the fields are pinned when your struct is.
199-
//! 4. You must make sure that you uphold the [`Drop` guarantee][drop-guarantee]:
199+
//! In particular, as explained previously, this means that your wrapper type must *not*
200+
//! be `#[repr(packed)]`.
201+
//! 3. You must make sure that you uphold the [`Drop` guarantee][drop-guarantee]:
200202
//! once your wrapper is pinned, the memory that contains the
201203
//! content is not overwritten or deallocated without calling the content's destructors.
202204
//! This can be tricky, as witnessed by `VecDeque`: the destructor of `VecDeque` can fail
203205
//! to call `drop` on all elements if one of the destructors panics. This violates the
204206
//! `Drop` guarantee, because it can lead to elements being deallocated without
205207
//! their destructor being called. (`VecDeque` has no pinning projections, so this
206208
//! does not cause unsoundness.)
207-
//! 5. You must not offer any other operations that could lead to data being moved out of
209+
//! 4. You must not offer any other operations that could lead to data being moved out of
208210
//! the fields when your type is pinned. This is usually not a concern, but can become
209211
//! tricky when interior mutability is involved. For example, imagine if `RefCell`
210212
//! had a method `fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T>`.
@@ -222,13 +224,20 @@
222224
//! reference we got later.
223225
//!
224226
//! On the other hand, if you decide *not* to offer any pinning projections, you
225-
//! are free to `impl<T> Unpin for Container<T>`. In the standard library,
227+
//! do not have to do anything. If your type also does not do any pinning itself,
228+
//! you are free to `impl<T> Unpin for Wrapper<T>`. In the standard library,
226229
//! this is done for all pointer types: `Box<T>: Unpin` holds for all `T`.
227230
//! It makes sense to do this for pointer types, because moving the `Box<T>`
228231
//! does not actually move the `T`: the `Box<T>` can be freely movable even if the `T`
229232
//! is not. In fact, even `Pin<Box<T>>` and `Pin<&mut T>` are always `Unpin` themselves,
230233
//! for the same reason.
231234
//!
235+
//! Another case where you might want to have a wrapper without structural pinning is when even
236+
//! a pinned wrapper lets its contents move, e.g. with a `take`-like operation. And, finally,
237+
//! if it is not possible to satisfy the requirements for structural pinning, it makes sense
238+
//! to add the `impl<T> Unpin for Wrapper<T>` to explicitly document this fact, and to let
239+
//! library clients benefit from the easier interaction with [`Pin`] that [`Unpin`] types enjoy.
240+
//!
232241
//! [`Pin`]: struct.Pin.html
233242
//! [`Unpin`]: ../../std/marker/trait.Unpin.html
234243
//! [`Deref`]: ../../std/ops/trait.Deref.html

0 commit comments

Comments
 (0)