Skip to content

Commit 56c1c35

Browse files
author
boats
committed
Detailed design of immovability API.
1 parent f8a83dd commit 56c1c35

File tree

1 file changed

+313
-0
lines changed

1 file changed

+313
-0
lines changed

text/0000-pin_and_move.md

Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
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

Comments
 (0)