Skip to content

Commit 1b556f1

Browse files
committed
expand pinning projections
1 parent 06b2aff commit 1b556f1

File tree

1 file changed

+41
-38
lines changed

1 file changed

+41
-38
lines changed

src/libcore/pin.rs

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -182,46 +182,49 @@
182182
//! is entirely up to the author of any given type. For many types, both answers are reasonable
183183
//! (e.g., there could be a version of `Vec` with structural pinning and another
184184
//! 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.
185+
//! If the type should have pinning projections, pinning must be structural.
187186
//! However, structural pinning comes with a few extra requirements:
188187
//!
189-
//! 1. The wrapper must only be [`Unpin`] if all the fields one can project to are
190-
//! `Unpin`. This is the default, but `Unpin` is a safe trait, so as the author of
191-
//! the wrapper it is your responsibility *not* to add something like
192-
//! `impl<T> Unpin for Wrapper<T>`. (Notice that adding a projection operation
193-
//! requires unsafe code, so the fact that `Unpin` is a safe trait does not break
194-
//! the principle that you only have to worry about any of this if you use `unsafe`.)
195-
//! 2. The destructor of the wrapper must not move out of its argument. This is the exact
196-
//! point that was raised in the [previous section][drop-impl]: `drop` takes `&mut self`,
197-
//! but the wrapper (and hence its fields) might have been pinned before.
198-
//! You have to guarantee that you do not move a field inside your `Drop` implementation.
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]:
202-
//! once your wrapper is pinned, the memory that contains the
203-
//! content is not overwritten or deallocated without calling the content's destructors.
204-
//! This can be tricky, as witnessed by `VecDeque`: the destructor of `VecDeque` can fail
205-
//! to call `drop` on all elements if one of the destructors panics. This violates the
206-
//! `Drop` guarantee, because it can lead to elements being deallocated without
207-
//! their destructor being called. (`VecDeque` has no pinning projections, so this
208-
//! does not cause unsoundness.)
209-
//! 4. You must not offer any other operations that could lead to data being moved out of
210-
//! the fields when your type is pinned. This is usually not a concern, but can become
211-
//! tricky when interior mutability is involved. For example, imagine if `RefCell`
212-
//! had a method `fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T>`.
213-
//! Then we could do the following:
214-
//! ```compile_fail
215-
//! fn exploit_ref_cell<T>(rc: Pin<&mut RefCell<T>) {
216-
//! { let p = rc.as_mut().get_pin_mut(); } // here we get pinned access to the `T`
217-
//! let rc_shr: &RefCell<T> = rc.into_ref().get_ref();
218-
//! let b = rc_shr.borrow_mut();
219-
//! let content = &mut *b; // and here we have `&mut T` to the same data
220-
//! }
221-
//! ```
222-
//! This is catastrophic, it means we can first pin the content of the `RefCell`
223-
//! (using `RefCell::get_pin_mut`) and then move that content using the mutable
224-
//! reference we got later.
188+
//! 1. The wrapper must only be [`Unpin`] if all the fields one can project to are
189+
//! `Unpin`. This is the default, but `Unpin` is a safe trait, so as the author of
190+
//! the wrapper it is your responsibility *not* to add something like
191+
//! `impl<T> Unpin for Wrapper<T>`. (Notice that adding a projection operation
192+
//! requires unsafe code, so the fact that `Unpin` is a safe trait does not break
193+
//! the principle that you only have to worry about any of this if you use `unsafe`.)
194+
//! 2. The destructor of the wrapper must not move out of its argument. This is the exact
195+
//! point that was raised in the [previous section][drop-impl]: `drop` takes `&mut self`,
196+
//! but the wrapper (and hence its fields) might have been pinned before.
197+
//! You have to guarantee that you do not move a field inside your `Drop` implementation.
198+
//! In particular, as explained previously, this means that your wrapper type must *not*
199+
//! be `#[repr(packed)]`.
200+
//! 3. You must make sure that you uphold the [`Drop` guarantee][drop-guarantee]:
201+
//! once your wrapper is pinned, the memory that contains the
202+
//! content is not overwritten or deallocated without calling the content's destructors.
203+
//! This can be tricky, as witnessed by `VecDeque`: the destructor of `VecDeque` can fail
204+
//! to call `drop` on all elements if one of the destructors panics. This violates the
205+
//! `Drop` guarantee, because it can lead to elements being deallocated without
206+
//! their destructor being called. (`VecDeque` has no pinning projections, so this
207+
//! does not cause unsoundness.)
208+
//! 4. You must not offer any other operations that could lead to data being moved out of
209+
//! the fields when your type is pinned. For example, if the wrapper contains an
210+
//! `Option<T>` and there is an operation such as `fn(Pin<&mut Wrapper<T>>) -> Option<T>`,
211+
//! that operation can be used to move a `T` out of a pinned `Wrapper` -- that means
212+
//! pinning cannot be structural.
213+
//!
214+
//! For a more complex example of moving data out of a pinnd type, imagine if `RefCell`
215+
//! had a method `fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T>`.
216+
//! Then we could do the following:
217+
//! ```compile_fail
218+
//! fn exploit_ref_cell<T>(rc: Pin<&mut RefCell<T>) {
219+
//! { let p = rc.as_mut().get_pin_mut(); } // here we get pinned access to the `T`
220+
//! let rc_shr: &RefCell<T> = rc.into_ref().get_ref();
221+
//! let b = rc_shr.borrow_mut();
222+
//! let content = &mut *b; // and here we have `&mut T` to the same data
223+
//! }
224+
//! ```
225+
//! This is catastrophic, it means we can first pin the content of the `RefCell`
226+
//! (using `RefCell::get_pin_mut`) and then move that content using the mutable
227+
//! reference we got later.
225228
//!
226229
//! On the other hand, if you decide *not* to offer any pinning projections, you
227230
//! do not have to do anything. If your type also does not do any pinning itself,

0 commit comments

Comments
 (0)