Skip to content

Commit 557c18e

Browse files
aturonboats
authored andcommitted
Add guide
1 parent 2cc1a6b commit 557c18e

File tree

1 file changed

+36
-3
lines changed

1 file changed

+36
-3
lines changed

text/0000-pin_and_move.md

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,41 @@ This proposal adds an API to std which would allow you to guarantee that a
2424
particular value will never move again, enabling safe APIs that rely on
2525
self-references to exist.
2626

27-
# Explanation
28-
[explanation]: #explanation
27+
# Guide-level explanation
28+
29+
The core goal of this RFC is to **provide a reference type where the referent is guaranteed to never move before being dropped**. We want to do this with a minimum disruption to the type system, and in fact, this RFC shows that we can achieve the goal without *any* type system changes.
30+
31+
Let's take that goal apart, piece by piece, from the perspective of the futures (i.e. async/await) use case:
32+
33+
- **Reference type**. The reason we need a reference type is that, when working with things like futures, we generally want to combine smaller futures into larger ones, and only at the top level put an entire resulting future into some immovable location. Thus, we need a reference type for methods like `poll`, so that we can break apart a large future into its smaller components, while retaining the guarantee about immobility.
34+
35+
- **Never to move before being dropped**. Again looking at the futures case, once we being `poll`ing a future, we want it to be able to store references into itself, which is possible if we can guarantee that the whole future will never move. We don't try to track *whether* such references exist at the type level, since that would involve cumbersome typestate; instead, we simply decree that by the time you initially `poll`, you promise to never move an immobile future again.
36+
37+
At the same time, we want to support futures (and iterators, etc.) that *can* move. While it's possible to do so by providing two distinct `Future` (or `Iterator`, etc) traits, such designs incur unacceptable ergonomic costs.
38+
39+
The key insight of this RFC is that we can create a new library type, `Pin<'a, T>`, which encompasses *both* moveable and immobile referents. The type is paired with a new auto trait, `Move`, which determines the meaning of `Pin<'a, T>`:
40+
41+
- If `T: Move` (which is the default), then `Pin<'a, T>` is entirely equivalent to `&'a mut T`.
42+
- If `T: !Move`, then `Pin<'a, T>` provides a unique reference to a `T` with lifetime `'a`, but only provides `&'a T` access safely. It also guarantees that the referent will *never* be moved. However, getting `&'a mut T` access is unsafe, because operations like `mem::replace` mean that `&mut` access is enough to move data out of the referent; you must promise not to do so.
43+
44+
To be clear: the *sole* function of `Move` is to control the meaning of `Pin`. Making `Move` an auto trait means that the vast majority of types are automatically "movable", so `Pin` degenerates to `&mut`. In the case that you need immobility, you *opt out* of `Move`, and then `Pin` becomes meaningful for your type.
45+
46+
Putting this all together, we arrive at the following definition of `Future`:
47+
48+
```rust
49+
trait Future {
50+
type Item;
51+
type Error;
52+
53+
fn poll(self: Pin<Self>, cx: task::Context) -> Poll<Self::Item, Self::Error>;
54+
}
55+
```
56+
57+
By default when implementing `Future` for a struct, this definition is equivalent to today's, which takes `&mut self`. But if you want to allow self-referencing in your future, you just opt out of `Move`, and `Pin` takes care of the rest.
58+
59+
The final piece of this RFC is the `Anchor` type, which is just a `Box` that doesn't allow moving out, but *does* allow you to acquire a `Pin` reference.
60+
61+
# Reference-level explanation
2962

3063
## The `Move` auto trait
3164

@@ -50,7 +83,7 @@ enforced through library APIs which use `Move` as a marker.
5083
An anchor is a new kind of smart pointer. It is very much like a box - it is a
5184
heap-allocated, exclusive-ownership type - but it provides additional
5285
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
86+
possible to mutably dereference the `Anchor` or move out of it in safe code. It does
5487
implement immutable Deref for all types, including types that don't implement
5588
`Move`.
5689

0 commit comments

Comments
 (0)