|
149 | 149 | //! of your type could have been pinned, you must treat Drop as implicitly taking
|
150 | 150 | //! `Pin<&mut Self>`.
|
151 | 151 | //!
|
| 152 | +//! In particular, if your type is `#[repr(packed)]`, the compiler will automatically |
| 153 | +//! move fields around to be able to drop them. As a consequence, you cannot use |
| 154 | +//! pinning with a `#[repr(packed)]` type. |
| 155 | +//! |
152 | 156 | //! # Projections and Structural Pinning
|
153 | 157 | //!
|
154 | 158 | //! One interesting question arises when considering the interaction of pinning and
|
155 |
| -//! the fields of a struct. When can a struct have a "projection operation", i.e., |
| 159 | +//! the fields of a struct. When can a struct have a "pinning projection", i.e., |
156 | 160 | //! an operation with type `fn(Pin<&[mut] Struct>) -> Pin<&[mut] Field>`?
|
157 | 161 | //! In a similar vein, when can a container type (such as `Vec`, `Box`, or `RefCell`)
|
158 | 162 | //! have an operation with type `fn(Pin<&[mut] Container<T>>) -> Pin<&[mut] T>`?
|
159 | 163 | //!
|
160 | 164 | //! This question is closely related to the question of whether pinning is "structural":
|
161 |
| -//! when you have pinned a wrapper type, have you pinned its contents? Adding a |
| 165 | +//! when you have pinned a wrapper type, have you pinned its contents? Deciding this |
| 166 | +//! is entirely up to the author of any given type. However, adding a |
162 | 167 | //! projection to the API answers that question with a "yes" by offering pinned access
|
163 |
| -//! to the contents. |
164 |
| -//! |
165 |
| -//! In general, as the author of a type you get to decide whether pinning is structural, and |
166 |
| -//! whether projections are provided. However, there are a couple requirements to be |
167 |
| -//! upheld when adding projection operations: |
| 168 | +//! to the contents. In that case, there are a couple requirements to be upheld: |
168 | 169 | //!
|
169 | 170 | //! 1. The wrapper must only be [`Unpin`] if all the fields one can project to are
|
170 | 171 | //! `Unpin`. This is the default, but `Unpin` is a safe trait, so as the author of
|
|
185 | 186 | //! This can be tricky, as witnessed by `VecDeque`: the destructor of `VecDeque` can fail
|
186 | 187 | //! to call `drop` on all elements if one of the destructors panics. This violates the
|
187 | 188 | //! `Drop` guarantee, because it can lead to elements being deallocated without
|
188 |
| -//! their destructor being called. |
| 189 | +//! their destructor being called. (`VecDeque` has no pinning projections, so this |
| 190 | +//! does not cause unsoundness.) |
189 | 191 | //! 5. You must not offer any other operations that could lead to data being moved out of
|
190 | 192 | //! the fields when your type is pinned. This is usually not a concern, but can become
|
191 | 193 | //! tricky when interior mutability is involved. For example, imagine `RefCell`
|
192 | 194 | //! would have a method `fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T>`.
|
193 |
| -//! This would be catastrophic, because it is possible to move out of a pinned |
194 |
| -//! `RefCell`: from `x: Pin<&mut RefCell<T>>`, use `let y = x.into_ref().get_ref()` to obtain |
195 |
| -//! `y: &RefCell<T>`, and from there use `y.borrow_mut().deref_mut()` to obtain `&mut T` |
196 |
| -//! which can be used with [`mem::swap`]. |
| 195 | +//! Then we could do the following: |
| 196 | +//! ```ignore |
| 197 | +//! fn exploit_ref_cell<T>(rc: Pin<&mut RefCell<T>) { |
| 198 | +//! { let p = rc.as_mut().get_pin_mut(); } // here we get pinned access to the `T` |
| 199 | +//! let rc_shr: &RefCell<T> = rc.into_ref().get_ref(); |
| 200 | +//! let b = rc_shr.borrow_mut(); |
| 201 | +//! let content = &mut *b; // and here we have `&mut T` to the same data |
| 202 | +//! } |
| 203 | +//! ``` |
| 204 | +//! This is catastrophic, it means we can first pin the content of the `RefCell` |
| 205 | +//! (using `RefCell::get_pin_mut`) and then move that content using the mutable |
| 206 | +//! reference we got later. |
197 | 207 | //!
|
198 | 208 | //! On the other hand, if you decide *not* to offer any pinning projections, you
|
199 | 209 | //! are free to `impl<T> Unpin for Container<T>`. In the standard library,
|
|
0 commit comments