Skip to content

Commit 2cc1a6b

Browse files
author
boats
committed
Reduce scope of pin/move RFC.
1 parent 1172a5d commit 2cc1a6b

File tree

1 file changed

+81
-207
lines changed

1 file changed

+81
-207
lines changed

text/0000-pin_and_move.md

Lines changed: 81 additions & 207 deletions
Original file line numberDiff line numberDiff line change
@@ -39,115 +39,63 @@ A type implements `Move` if in its stack representation, it does not contain
3939
internal references to other positions within its stack representation. Nearly
4040
every type in Rust is `Move`.
4141

42-
Positive impls of `Move` are added for types which contain pointers to generic
43-
types, but do not contain those types in their stack representation, e.g:
44-
45-
```rust
46-
unsafe impl<'a, T: ?Sized> Move for &'a T { }
47-
unsafe impl<'a, T: ?Sized> Move for &'a mut T { }
48-
unsafe impl<'a, T: ?Sized> Move for Box<T> { }
49-
unsafe impl<"a, T> Move for Vec<T> { }
50-
// etc
51-
```
52-
5342
This trait is a lang item, but only to generate negative impls for certain
5443
generators. Unlike previous `?Move` proposals, and unlike some traits like
55-
`Sized` and `Copy`, this trait does not impose any particular semantics on
56-
types that do or don't implement it.
57-
58-
## The stability marker traits
44+
`Sized` and `Copy`, this trait does not impose any compiler-based semantics
45+
types that do or don't implement it. Instead, the semantics are entirely
46+
enforced through library APIs which use `Move` as a marker.
5947

60-
A hierarchy of marker traits for smart pointer types is added to `core::marker`
61-
and `std::marker`. These exist to provide a shared language for talking about
62-
the guarantees that different smart pointers provide. This enables both the
63-
kinds of self-referential support we talk about later in this RFC and other
64-
APIs like rental and owning-ref.
48+
## The `Anchor` type
6549

66-
### `Own` and `Share`
50+
An anchor is a new kind of smart pointer. It is very much like a box - it is a
51+
heap-allocated, exclusive-ownership type - but it provides additional
52+
constraints. Unless the type it references implements `Move`, it is not
53+
possible to mutably dereference the `Anchor` or move out of it. It does
54+
implement immutable Deref for all types, including types that don't implement
55+
`Move`.
6756

6857
```rust
69-
unsafe trait Own: Deref { }
70-
unsafe trait Share: Deref + Clone { }
71-
```
58+
#[fundamental]
59+
struct Anchor<T: ?Sized> {
60+
inner: Box<T>,
61+
}
62+
63+
impl<T> Anchor<T> {
64+
pub fn new(data: T) -> Anchor<T>;
65+
}
7266

73-
These two traits are for smart pointers which implement some form of ownership
74-
construct.
67+
impl<T: ?Sized> Anchor<T> {
68+
pub fn as_pin<'a>(&'a mut self) -> Pin<'a, T>;
7569

76-
- **Own** implies that this type has unique ownership over the data which it
77-
dereferences to. That is, unless the data is moved out of the smart pointer,
78-
when this pointer is destroyed, so too will that data. Examples of `Own`
79-
types are `Box<T>`, `Vec<T>` and `String`.
80-
- **Share** implies that this type has shared ownership over the data which it
81-
dereferences to. It implies `Clone`, and every type it is `Clone`d it must
82-
continue to refer to the same data; it cannot perform deep clones of that
83-
data. Examples of `Share` types are `Rc<T>` and `Arc<T>`.
70+
pub unsafe fn get_mut(this: &mut Anchor<T>) -> &mut T;
8471

85-
These traits are mutually exclusive - it would be a logic error to implement
86-
both of them for a single type. We retain the liberty to assume that no type
87-
ever does implement both - we could upgrade this from a logic error to
88-
undefined behavior, we could make changes that would break any code that
89-
implements both traits for the same type.
72+
pub unsafe fn into_inner_unchecked(this: Anchor<T>) -> T;
73+
}
9074

91-
### `StableDeref` and `StableDerefMut`
75+
impl<T: Move + ?Sized> Anchor<T> {
76+
pub unsafe fn into_inner(this: Anchor<T>) -> T;
77+
}
9278

93-
```rust
94-
unsafe trait StableDeref: Deref { }
95-
unsafe trait StableDerefMut: StableDeref + DerefMut { }
79+
impl<T: ?Sized> Deref for Anchor<T> {
80+
type Target = T;
81+
}
82+
83+
impl<T: Move + ?Sized> DerefMut for Anchor<T> { }
84+
85+
unsafe impl<T: ?Sized> Move for Anchor<T> { }
9686
```
9787

98-
These two traits are for any pointers which guarantee that the type they
99-
dereference to is at a stable address. That is, moving the pointer does not
100-
move the type being addressed.
101-
102-
- **StableDeref** implies that the referenced data will not move if you move
103-
this type or dereference it *immutably*. Types that implement this include
104-
`Box<T>`, both reference types, `Rc<T>`, `Arc<T>`, `Vec<T>`, and `String`.
105-
Pretty much everything in std that implements `Deref` implements
106-
`StableDeref`.
107-
- **StableDerefMut** implies the same guarantees as `StableDeref`, but also
108-
guarantees that dereferencing a *mutable* reference will not cause the
109-
referenced data to change addresses. Because of this, it also implies
110-
`DerefMut`. Examples of type that implement this include `&mut T`, `Box<T>`,
111-
and `Vec<T>`.
112-
113-
Note that `StableDerefMut` does not imply that taking a mutable reference to
114-
the smart pointer will not cause the referenced data to move. For example,
115-
calling `push` on a `Vec` can cause the slice it dereferences to to change
116-
locations. Its only obtaining a mutable reference to the target data which is
117-
guaranteed not to relocate it.
118-
119-
Note also that this RFC does not propose implementing `StableDerefMut` for
120-
`String`. This is to be forward compatible with the static string optimization,
121-
an optimization which allows values of `&'static str` to be converted to
122-
`String` without incurring a heap allocation. A component of this optimization
123-
would cause `String` to allocate when dereferencing to an `&mut str` if the
124-
backing data would otherwise be in rodata.
125-
126-
### Notes on existing ecosystem traits
127-
128-
These traits supplant certain traits in the ecosystem which already provide
129-
similar guarantees. In particular, the [stable_deref_trait][stable-deref]
130-
crate provides a similar bit different hierarchy. The differences are:
131-
132-
- That crate draws no distinction between `StableDeref` and `StableDerefMut`.
133-
This does not leave forward compatibility for the static string optimization
134-
mentioned previously.
135-
- That crate has no equivalent to the `Own` trait, which is necessary for some
136-
APIs using internal references.
137-
- That crate has a `StableDerefClone` type, which is equivalent to the bound
138-
`Share + StableDeref` in our system.
139-
140-
If the hierarchy proposed in this RFC becomes stable, all users are encouraged
141-
to migrate from that crate to the standard library traits.
88+
For types that do not implement `Move`, instead of using mutable references,
89+
users of `Anchor` will use the `Pin` type, described in the next section.
14290

14391
## The `Pin` type
14492

14593
The `Pin` type is a wrapper around a mutable reference. If the type it
14694
references is `!Move`, the `Pin` type guarantees that the referenced data will
147-
never be moved again. It has a relatively small API. It is added to both
148-
`std::mem` and `core::mem`.
95+
never be moved again.
14996

15097
```rust
98+
#[fundamental]
15199
struct Pin<'a, T: ?Sized + 'a> {
152100
data: &'a mut T,
153101
}
@@ -173,104 +121,16 @@ impl<'a, T: ?Sized + Move> DerefMut for Pin<'a, T> { }
173121

174122
For types which implement `Move`, `Pin` is essentially the same as an `&mut T`.
175123
But for types which do not, the conversion between `&mut T` and `Pin` is
176-
unsafe (however, `Pin` can be easily immutably dereferenced, even for `!Move`
177-
types).
124+
unsafe.
178125

179126
The contract on the unsafe part of `Pin`s API is that a Pin cannot be
180127
constructed if the data it references would ever move again, and that it cannot
181128
be converted into a mutable reference if the data might ever be moved out of
182129
that reference. In other words, if you have a `Pin` containing data which does
183130
not implement `Move`, you have a guarantee that that data will never move.
184131

185-
The next two subsections describe safe APIs for constructing a `Pin` of data
186-
which cannot be moved - one in the heap, and one in the stack.
187-
188-
### Pinning to the heap: The `Anchor` type
189-
190-
The `Anchor` wrapper takes a type that implements `StableDeref` and `Own`, and
191-
prevents users from moving data out of that unless it implements `Move`. It is
192-
added to `std::mem` and `core::mem`.
193-
194-
```rust
195-
struct Anchor<T> {
196-
ptr: T,
197-
}
198-
199-
impl<T: StableDerefMut + Own> Anchor<T> {
200-
pub fn new(ptr: T) -> Anchor<T>;
201-
202-
pub unsafe fn get_mut(this: &mut Anchor<T>) -> &mut T;
203-
204-
pub unsafe fn into_inner_unchecked(this: Anchor<T>) -> T;
205-
206-
pub fn pin<'a>(this: &'a mut Anchor<T>) -> Pin<'a, T::Target>;
207-
}
208-
209-
impl<T: StableDerefMut + Own> Anchor<T> where T::Target: Move {
210-
pub fn into_inner(this: Anchor<T>) -> T;
211-
}
212-
213-
impl<T: StableDerefMut + Own> Deref for Anchor<T> {
214-
type Target = T;
215-
}
216-
217-
impl<T: StableDerefMut + Own> DerefMut for Anchor<T> where T::Target: Move { }
218-
```
219-
220-
Because `Anchor` implements `StableDeref` and `Own`, and it is not safe to get
221-
an `&mut T` if the target of `T ` does not implement `Move`, an anchor
222-
guarantees that the target of `T` will never move again. This satisfies the
223-
safety constraints of `Pin`, allowing a user to construct a `Pin` from an
224-
anchored pointer.
225-
226-
Because the data is anchored into the heap, you can move the anchor around
227-
without moving the data itself. This makes anchor a very flexible way to handle
228-
immovable data, at the cost of a heap allocation.
229-
230-
An example use:
231-
232-
```rust
233-
let mut anchor = Anchor::new(Box::new(immovable_data));
234-
let pin = Anchor::pin(&mut anchor);
235-
```
236-
237-
### Pinning to the stack: `Pin::stack` and `pinned`
238-
239-
Data can also be pinned to the stack. This avoids the heap allocation, but the
240-
pin must not outlive the data being pinned, and the API is less convenient.
241-
242-
First, the pinned function, added to `std::mem` and `core::mem`:
243-
244-
```rust
245-
pub struct StackPinned<'a, T: ?Sized> {
246-
_marker: PhantomData<&'a mut &'a ()>,
247-
data: T
248-
}
249-
250-
pub fn pinned<'a, T>(data: T) -> StackPinned<'a, T> {
251-
StackPinned {
252-
data, _marker: PhantomData,
253-
}
254-
}
255-
```
256-
257-
Second, this constructor is added to `Pin`:
258-
259-
```rust
260-
impl<'a, T: ?Sized> Pin<'a, T> {
261-
pub fn stack(data: &'a mut StackPinned<'a, T>) -> Pin<'a, T>;
262-
}
263-
```
264-
265-
Because the lifetime of the `StackPinned` and the lifetime of the reference to
266-
it are bound together, the StackPinned wrapper is functionally moved (and with
267-
it the data inside it) into the Pin. Thus, even though the data is allocated on
268-
the stack, it is pinned to its location for the remainder of its scope.
269-
270-
```rust
271-
let mut data = mem::pinned(immovable_data);
272-
let pin = Pin::stack(&mut data);
273-
```
132+
The `Anchor` type has a method `as_pin`, which returns a `Pin`, because it
133+
upholds the guarantees necessary to safely construct a `Pin`.
274134

275135
## Immovable generators
276136

@@ -291,24 +151,12 @@ changes to the generator API:
291151
This is an example of how the APIs in this RFC allow for self-referential data
292152
types to be created safely.
293153

294-
## Stabilization planning
295-
296-
This RFC proposes a large API addition, and so it is broken down into four
297-
separate feature flags, which can be stabilized in stages:
298-
299-
1. `stable_deref` - to control the smart pointer trait hierarchy - StableDeref,
300-
StableDerefMut, Own, and Share.
301-
2. `pin_and_move` - to control the `Move` auto trait and the `Pin` type. These
302-
two components only make sense working together.
303-
3. `anchor` - to control the `Anchor` struct, pinning to the heap.
304-
4. `stack_pinning` - to control the APIs related to stack pinning.
305-
306154
# Drawbacks
307155
[drawbacks]: #drawbacks
308156

309-
This adds additional APIs to std, including several marker traits and an auto
310-
trait. Such additions should not be taken lightly, and only included if they
311-
are well-justified by the abstractions they express.
157+
This adds additional APIs to std, including an auto trait. Such additions
158+
should not be taken lightly, and only included if they are well-justified by
159+
the abstractions they express.
312160

313161
# Rationale and alternatives
314162
[alternatives]: #alternatives
@@ -353,21 +201,47 @@ burden on every user who wants to call resume to guarantee (at the risk of
353201
memory insafety) that their types were not moved, or that they were moveable.
354202
This seemed like a worse trade off than adding these APIs.
355203

356-
## Relationship to owning-ref & rental
204+
## Anchor as a wrapper type and `StableDeref`
205+
206+
In a previous iteration of this RFC, `Anchor` was a wrapper type that could
207+
"anchor" any smart pointer, and there was a hierarchy of traits relating to the
208+
stability of the referent of different pointer types.
209+
210+
The primary benefit of this approach was that it was partially integrated with
211+
crates like owning-ref and rental, which also use a hierarchy of stability
212+
traits. However, because of differences in the requirements, the traits used by
213+
owning-ref et al ended up being a non-overlapping subset of the traits proposed
214+
by this RFC from the traits used by the Anchor type. Merging these into a
215+
single hierarchy provided relatively little benefit.
216+
217+
And the only types that implemented all of the necessary traits to be put into
218+
an Anchor before were `Box<T>` and `Vec<T>`. Because you cannot get mutable
219+
access to the smart pointer (unless the referent implements `Move`), an
220+
`Anchor<Vec<T>>` was not really any different from an `Anchor<Box<[T]>>` in the
221+
previous iteration of the RFC. For this reason, making `Anchor` always a box,
222+
and just supporting `Anchor<[T]>`, reduced the API complexity without losing
223+
any expressiveness.
224+
225+
## Stack pinning API (potential future extension)
226+
227+
This API supports "anchoring" `!Move` types in the heap. However, they can also
228+
be safely held in place in the stack, allowing a safe API for creating a `Pin`
229+
referencing a stack allocated `!Move` type.
230+
231+
This API is small, and does not become a part of anyone's public API. For that
232+
reason, we'll start by allowing it to grow out of tree, in third party crates,
233+
before including it in std.
357234

358-
Existing crates like owning-ref and rental make some use of "self-referential"
359-
types. Unlike the generators this RFC is designed to support, their references
360-
always point into the heap - making it acceptable to move their types around.
235+
## Making `Pin` a built-in type (potential future extension)
361236

362-
However, some of this infrastructure is still useful to those crates. In
363-
particular, the stable deref hierarchy is related to the existing hierarchy in
364-
the stable_deref crate, which those other crates depend on. By uplifting those
365-
markers into the standard library, we create a shared, endorsed, and guaranteed
366-
set of markers for the invariants those libraries care about.
237+
The `Pin` type could instead be a new kind of first-class reference - `&'a pin
238+
T`. This would have some advantages - it would be trivial to project through
239+
fields, for example, and "stack pinning" would not require an API, it would be
240+
natural. However, it has the downside of adding a new reference type, a very
241+
big language change.
367242

368-
In order to be implemented in safe code, those library need additional features
369-
connecting to "existential" or "generative" lifetimes. These language changes
370-
are out of scope for this RFC.
243+
For now, we're happy to stick with the `Pin` struct in std, and if this type is
244+
ever added, turn the `Pin` type into an alias for the reference type.
371245

372246
# Unresolved questions
373247
[unresolved]: #unresolved-questions

0 commit comments

Comments
 (0)