|
| 1 | +- Feature Name: pin_and_move |
| 2 | +- Start Date: 2018-02-19 |
| 3 | +- RFC PR: (leave this empty) |
| 4 | +- Rust Issue: (leave this empty) |
| 5 | + |
| 6 | +# Summary |
| 7 | +[summary]: #summary |
| 8 | + |
| 9 | +Introduce new APIs to libcore / libstd to serve as safe abstractions for data |
| 10 | +which cannot be safely moved around. |
| 11 | + |
| 12 | +# Motivation |
| 13 | +[motivation]: #motivation |
| 14 | + |
| 15 | +Why are we doing this? What use cases does it support? What is the expected outcome? |
| 16 | + |
| 17 | +# Explanation |
| 18 | +[explanation]: #explanation |
| 19 | + |
| 20 | +## The `Move` auto trait |
| 21 | + |
| 22 | +This new auto trait is added to the `core::marker` and `std::marker` modules: |
| 23 | + |
| 24 | +```rust |
| 25 | +pub unsafe auto trait Move { } |
| 26 | +``` |
| 27 | + |
| 28 | +A type implements `Move` if in its stack representation, it contains internal |
| 29 | +references to other positions within its strack representation. Nearly every |
| 30 | +type in Rust is `Move`. |
| 31 | + |
| 32 | +Positive impls of `Move` are added for types which contain pointers to generic |
| 33 | +types, but do not contain those types in their stack representation, e.g: |
| 34 | + |
| 35 | +```rust |
| 36 | +unsafe impl<'a, T: ?Sized> Move for &'a T { } |
| 37 | +unsafe impl<'a, T: ?Sized> Move for &'a mut T { } |
| 38 | +unsafe impl<'a, T: ?Sized> Move for Box<T> { } |
| 39 | +unsafe impl<"a, T> Move for Vec<T> { } |
| 40 | +// etc |
| 41 | +``` |
| 42 | +
|
| 43 | +This trait is a lang item, but only to generate negative impls for certain |
| 44 | +generators. Unlike previous `?Move` proposals, and unlike some traits like |
| 45 | +`Sized` and `Copy`, this trait does not impose any particular semantics on |
| 46 | +types that do or don't implement it. |
| 47 | +
|
| 48 | +## The stability marker traits |
| 49 | +
|
| 50 | +A hierarchy of marker traits for smart pointer types is added to `core::marker` |
| 51 | +and `std::marker`. These exist to provide a shared language for talking about |
| 52 | +the guarantees that different smart pointers provide. This enables both the |
| 53 | +kinds of self-referential support we talk about later in this RFC and other |
| 54 | +APIs like rental and owning-ref. |
| 55 | +
|
| 56 | +### `Own` and `Share` |
| 57 | +
|
| 58 | +```rust |
| 59 | +unsafe trait Own: Deref { } |
| 60 | +unsafe trait Share: Deref + Clone { } |
| 61 | +``` |
| 62 | +
|
| 63 | +These two traits are for smart pointers which implement some form of ownership |
| 64 | +construct. |
| 65 | +
|
| 66 | +- **Own** implies that this type has unique ownership over the data which it |
| 67 | + dereferences to. That is, unless the data is moved out of the smart pointer, |
| 68 | + when this pointer is destroyed, so too will that data. Examples of `Own` |
| 69 | + types are `Box<T>`, `Vec<T>` and `String`. |
| 70 | +- **Share** implies that this type has shared ownership over the data which it |
| 71 | + dereferences to. It implies `Clone`, and every type it is `Clone`d it must |
| 72 | + continue to refer to the same data; it cannot perform deep clones of that |
| 73 | + data. Examples of `Share` |
| 74 | +
|
| 75 | +These traits are mutually exclusive - it would be a logic error to implement |
| 76 | +both of them for a single type. We retain the liberty to assume that no type |
| 77 | +ever does implement both - we could upgrade this from a logic error to |
| 78 | +undefined behavior, we could make changes that would break any code that |
| 79 | +implements both traits for the same type. |
| 80 | +
|
| 81 | +### `StableDeref` and `StableDerefMut` |
| 82 | +
|
| 83 | +```rust |
| 84 | +unsafe trait StableDeref: Deref { } |
| 85 | +unsafe trait StableDerefMut: StableDeref + DerefMut { } |
| 86 | +``` |
| 87 | +
|
| 88 | +These two traits are for any pointers which guarantee that the type they |
| 89 | +dereference to is at a stable address. That is, moving the pointer does not |
| 90 | +move the type being addressed. |
| 91 | +
|
| 92 | +- **StableDeref** implies that the referenced data will not move if you move |
| 93 | + this type or dereference it *immutably*. Types that implement this include |
| 94 | + `Box<T>`, both reference types, `Rc<T>`, `Arc<T>`, `Vec<T>`, and `String`. |
| 95 | + Pretty much everything in std that implements `Deref` implements |
| 96 | + `StableDeref`. |
| 97 | +- **StableDerefMut** implies the same guarantees as `StableDeref`, but also |
| 98 | + guarantees that dereferencing a *mutable* reference will not cause the |
| 99 | + referenced data to change addresses. Because of this, it also implies |
| 100 | + `DerefMut`. Examples of type that implement this include `&mut T`, `Box<T>`, |
| 101 | + and `Vec<T>`. |
| 102 | +
|
| 103 | +Note that `StableDerefMut` does not imply that taking a mutable reference to |
| 104 | +the smart pointer will not cause the referenced data to move. For example, |
| 105 | +calling `push` on a `Vec` can cause the slice it dereferences to to change |
| 106 | +locations. Its only obtaining a mutable reference to the target data which is |
| 107 | +guaranteed not to relocate it. |
| 108 | +
|
| 109 | +Note also that this RFC does not propose implementing `StableDerefMut` for |
| 110 | +`String`. This is to be forward compatible with the static string optimization, |
| 111 | +an optimization which allows values of `&'static str` to be converted to |
| 112 | +`String` without incurring a heap allocation. A component of this optimization |
| 113 | +would cause `String` to allocate when dereferencing to an `&mut str` if the |
| 114 | +backing data would otherwise be in rodata. |
| 115 | +
|
| 116 | +### Notes on existing ecosystem traits |
| 117 | +
|
| 118 | +These traits supplant certain traits in the ecosystem which already provide |
| 119 | +similar guarantees. In particular, the [stable-deref][stable-deref] crate |
| 120 | +provides a similar bit different hierarchy. The differences are: |
| 121 | +
|
| 122 | +- That crate draws no distinction between `StableDeref` and `StableDerefMut`. |
| 123 | + This does not leave forward compatibility for the static string optimization |
| 124 | + mentioned previously. |
| 125 | +- That crate has no equivalent to the `Own` trait, which is necessary for some |
| 126 | + APIs using internal references. |
| 127 | +- That crate has a `StableDerefClone` type, which is equivalent to the bound |
| 128 | + `Share + StableDeref` in our system. |
| 129 | +
|
| 130 | +If the hierarchy proposed in this RFC becomes stable, all users are encouraged |
| 131 | +to migrate from that crate to the standard library traits. |
| 132 | +
|
| 133 | +## The `Pin` type |
| 134 | +
|
| 135 | +The `Pin` type is a wrapper around a mutable reference. If the type it |
| 136 | +references is `!Move`, the `Pin` type guarantees that the referenced data will |
| 137 | +never be moved again. It has a relatively small API. It is added to both |
| 138 | +`std::mem` and `core::mem`. |
| 139 | +
|
| 140 | +```rust |
| 141 | +struct Pin<'a, T: ?Sized + 'a> { |
| 142 | + data: &'a mut T, |
| 143 | +} |
| 144 | +
|
| 145 | +impl<'a, T: ?Sized + Move> Pin<'a, T> { |
| 146 | + pub fn new(data: &'a mut T) -> Pin<'a, T>; |
| 147 | +} |
| 148 | +
|
| 149 | +impl<'a, T: ?Sized> Pin<'a, T> { |
| 150 | + pub unsafe fn new_unchecked(data: &'a mut T) -> Pin<'a, T>; |
| 151 | +
|
| 152 | + pub unsafe fn get_mut(this: Pin<'a, T>) -> &'a mut T; |
| 153 | +
|
| 154 | + pub fn borrow<'b>(this: &'b mut Pin<'a, T>) -> Pin<'b, T>; |
| 155 | +} |
| 156 | +
|
| 157 | +impl<'a, T: ?Sized> Deref for Pin<'a, T> { |
| 158 | + type Target = T; |
| 159 | +} |
| 160 | +
|
| 161 | +impl<'a, T: ?Sized + Move> DerefMut for Pin<'a, T> { } |
| 162 | +``` |
| 163 | +
|
| 164 | +For types which implement `Move`, `Pin` is essentially the same as an `&mut T`. |
| 165 | +But for types which do not, the conversion between `&mut T` and `Pin` is |
| 166 | +unsafe (however, `Pin` can be easily immutably dereferenced, even for `!Move` |
| 167 | +types). |
| 168 | +
|
| 169 | +The contract on the unsafe part of `Pin`s API is that a Pin cannot be |
| 170 | +constructed if the data it references would ever move again, and that it cannot |
| 171 | +be converted into a mutable reference if the data might ever be moved out of |
| 172 | +that reference. In other words, if you have a `Pin` containing data which does |
| 173 | +not implement `Move`, you have a guarantee that that data will never move. |
| 174 | +
|
| 175 | +The next two subsections describe safe APIs for constructing a `Pin` of data |
| 176 | +which cannot be moved - one in the heap, and one in the stack. |
| 177 | +
|
| 178 | +### Pinning to the heap: The `Anchor` type |
| 179 | +
|
| 180 | +The `Anchor` wrapper takes a type that implements `StableDeref` and `Own`, and |
| 181 | +prevents users from moving data out of that unless it implements `Move`. It is |
| 182 | +added to `std::mem` and `core::mem`. |
| 183 | +
|
| 184 | +```rust |
| 185 | +struct Anchor<T> { |
| 186 | + ptr: T, |
| 187 | +} |
| 188 | +
|
| 189 | +impl<T: StableDeref + Own> Anchor<T> { |
| 190 | + pub fn new(ptr: T) -> Anchor<T>; |
| 191 | +
|
| 192 | + pub unsafe fn get_mut(this: &mut Anchor<T>) -> &mut T; |
| 193 | +
|
| 194 | + pub unsafe fn into_inner_unchecked(this: Anchor<T>) -> T; |
| 195 | +
|
| 196 | + pub fn pin<'a>(this: &'a mut Anchor<T>) -> Pin<'a, T::Target>; |
| 197 | +} |
| 198 | +
|
| 199 | +impl<T: StableDeref + Own> Anchor<T> where T::Target: Move { |
| 200 | + pub fn into_inner(this: Anchor<T>) -> T; |
| 201 | +} |
| 202 | +
|
| 203 | +impl<T: StableDeref + Own> Deref for Anchor<T> { |
| 204 | + type Target = T; |
| 205 | +} |
| 206 | +
|
| 207 | +impl<T: StableDeref + Own> DerefMut for Anchor<T> where T::Target: Move { } |
| 208 | +``` |
| 209 | +
|
| 210 | +Because `Anchor` implements `StableDeref` and `Own`, and it is not safe to get |
| 211 | +an `&mut T` if the target of `T ` does not implement `Move`, an anchor |
| 212 | +guarantees that the target of `T` will never move again. This satisfies the |
| 213 | +safety constraints of `Pin`, allowing a user to construct a `Pin` from an |
| 214 | +anchored pointer. |
| 215 | +
|
| 216 | +Because the data is anchored into the heap, you can move the anchor around |
| 217 | +without moving the data itself. This makes anchor a very flexible way to handle |
| 218 | +immovable data, at the cost of a heap allocation. |
| 219 | +
|
| 220 | +An example use: |
| 221 | +
|
| 222 | +```rust |
| 223 | +let mut anchor = Anchor::new(Box::new(immovable_data)); |
| 224 | +let pin = Anchor::pin(&mut anchor); |
| 225 | +``` |
| 226 | +
|
| 227 | +### Pinning to the stack: `Pin::stack` and `pinned` |
| 228 | +
|
| 229 | +Data can also be pinned to the stack. This avoids the heap allocation, but the |
| 230 | +pin must not outlive the data being pinned, and the API is less convenient. |
| 231 | +
|
| 232 | +First, the pinned function, added to `std::mem` and `core::mem`: |
| 233 | +
|
| 234 | +```rust |
| 235 | +pub struct StackPinned<'a, T: ?Sized> { |
| 236 | + _marker: PhantomData<&'a mut &'a ()>, |
| 237 | + data: T |
| 238 | +} |
| 239 | +
|
| 240 | +pub fn pinned<'a, T>(data: T) -> StackPinned<'a, T> { |
| 241 | + StackPinned { |
| 242 | + data, _marker: PhantomData, |
| 243 | + } |
| 244 | +} |
| 245 | +``` |
| 246 | +
|
| 247 | +Second, this constructor is added to `Pin`: |
| 248 | +
|
| 249 | +```rust |
| 250 | +impl<'a, T: ?Sized> Pin<'a, T> { |
| 251 | + pub fn stack(data: &'a mut StackPinned<'a, T>) -> Pin<'a, T>; |
| 252 | +} |
| 253 | +``` |
| 254 | +
|
| 255 | +Because the lifetime of the `StackPinned` and the lifetime of the reference to |
| 256 | +it are bound together, the StackPinned wrapper is functionally moved (and with |
| 257 | +it the data inside it) into the Pin. Thus, even though the data is allocated on |
| 258 | +the stack, it is pinned to its location for the remainder of its scope. |
| 259 | +
|
| 260 | +```rust |
| 261 | +let mut data = mem::pinned(immovable_data); |
| 262 | +let pin = Pin::stack(&mut data); |
| 263 | +``` |
| 264 | +
|
| 265 | +## Immovable generators |
| 266 | +
|
| 267 | +Today, the unstable generators feature has an option to create generators which |
| 268 | +contain references that live across yield points - these are, in effect, |
| 269 | +internal references into the generator's state machine. Because internal |
| 270 | +references are invalidated if the type is moved, these kinds of generators |
| 271 | +("immovable generators") are currently unsafe to create. |
| 272 | +
|
| 273 | +Once the arbitrary_self_types feature becomes object safe, we will make three |
| 274 | +changes to the generator API: |
| 275 | +
|
| 276 | +1. We will change the `resume` method to take self by `self: Pin<Self>` instead |
| 277 | + of `&mut self`. |
| 278 | +2. We will implement `!Move` for the anonymous type of an immovable generator. |
| 279 | +3. We will make it safe to define an immovable generator. |
| 280 | +
|
| 281 | +This is an example of how the APIs in this RFC allow for self-referential data |
| 282 | +types to be created safely. |
| 283 | +
|
| 284 | +## Stabilization planning |
| 285 | +
|
| 286 | +This RFC proposes a large API addition, and so it is broken down into four |
| 287 | +separate feature flags, which can be stabilized in stages: |
| 288 | +
|
| 289 | +1. `stable_deref` - to control the smart pointer trait hierarchy - StableDeref, |
| 290 | + StableDerefMut, Own, and Share. |
| 291 | +2. `pin_and_move` - to control the `Move` auto trait and the `Pin` type. These |
| 292 | + two components only make sense working together. |
| 293 | +3. `anchor` - to control the `Anchor` struct, pinning to the heap. |
| 294 | +4. `stack_pinning` - to control the APIs related to stack pinning. |
| 295 | +
|
| 296 | +# Drawbacks |
| 297 | +[drawbacks]: #drawbacks |
| 298 | +
|
| 299 | +Why should we *not* do this? |
| 300 | +
|
| 301 | +# Rationale and alternatives |
| 302 | +[alternatives]: #alternatives |
| 303 | +
|
| 304 | +- Why is this design the best in the space of possible designs? |
| 305 | +- What other designs have been considered and what is the rationale for not choosing them? |
| 306 | +- What is the impact of not doing this? |
| 307 | +
|
| 308 | +# Unresolved questions |
| 309 | +[unresolved]: #unresolved-questions |
| 310 | +
|
| 311 | +- What parts of the design do you expect to resolve through the RFC process before this gets merged? |
| 312 | +- What parts of the design do you expect to resolve through the implementation of this feature before stabilization? |
| 313 | +- What related issues do you consider out of scope for this RFC that could be addressed in the future independently of the solution that comes out of this RFC? |
0 commit comments