Skip to content

Commit b64b66e

Browse files
author
Ariel Ben-Yehuda
committed
&move, DerefMove, DerefPure and box patterns
1 parent 8fb8742 commit b64b66e

File tree

1 file changed

+222
-0
lines changed

1 file changed

+222
-0
lines changed

text/0000-missing-derefs.md

+222
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
- Feature Name: missing_derefs
2+
- Start Date: 2016-06-09
3+
- RFC PR: (leave this empty)
4+
- Rust Issue: (leave this empty)
5+
6+
# Summary
7+
[summary]: #summary
8+
9+
Add `&move` pointers, the `DerefMove` trait, and the unsafe
10+
`DerefPure` traits. Allow using `DerefPure` derefs in lvalues.
11+
12+
# Motivation
13+
[motivation]: #motivation
14+
15+
Rust's `Box` has a few features that are not implementable by library
16+
traits: it is possible to match on `Box` with box patterns, and to
17+
move out of it.
18+
19+
User-defined types also want to make use of these features.
20+
21+
Also, it is not possible to use pattern matching on structures that
22+
contain smart pointers. We would want this to be possible.
23+
24+
# Detailed design
25+
[design]: #detailed-design
26+
27+
## DerefPure
28+
29+
Add a `DerefPure` trait:
30+
```Rust
31+
pub unsafe trait DerefPure : Deref {}
32+
```
33+
34+
Implmenenting the `DerefPure` trait tells the compiler that dereferences
35+
of the type it is implemented for behave like dereferences of normal
36+
pointers - as long as the receiver is borrowed, the compiler can merge,
37+
move and remove calls to the `Deref` methods, and the returned pointer
38+
will stay the same.
39+
40+
Also, the methods must not panic and (if `DerefMove` is implemented) may
41+
be called on a partially-initialized value.
42+
43+
If a type implements `DerefPure`, then user-defined dereferences of it
44+
are implemented with a `deref` lvalue projection as if they were a built-in
45+
pointer.
46+
47+
Types implementing `DerefPure` can be used in `box` patterns. This works
48+
like all the other reference patterns. For example, if `Vec` implements
49+
`DerefPure` and `BasicBlockData.statements` is a `Vec`:
50+
51+
```Rust
52+
match self.basic_blocks[*start] {
53+
BasicBlockData {
54+
statements: box [],
55+
terminator: ref mut terminator @ Some(Terminator {
56+
kind: TerminatorKind::Goto { .. }, ..
57+
}), ..
58+
} => { /* .. */ }
59+
_ => return
60+
};
61+
```
62+
63+
## &move
64+
65+
Add a new mutability `move`. `&move` references are references that own their
66+
contents, but not the memory they refer to.
67+
68+
When parsing a `move` closure, `&move |..` is parsed as `& (move |..` -
69+
as creating a `move` closure and taking an immutable reference to it, rather
70+
than creating a non-moving closure and taking an `&move` reference to it. Of
71+
course, you can force the other choice by explicit parentheses - `&move (|..`.
72+
73+
Unlike some other proposals, the [RFC1214] rules remain the same - a
74+
`&'a move T` reference requires that `T: 'a`. We may want to relax these
75+
rules.
76+
77+
`&move` references are tracked by the move checker just like ordinary
78+
values. They are linear - when they are dropped, their unmoved contents
79+
are dropped. It is possible to initialize/reinitialize them just like normal
80+
variables.
81+
82+
Outside of the move checker, `&move` references always have valid contents.
83+
If you want to create a temporary uninitialized `&move` reference, you can
84+
use `mem::forget`:
85+
86+
```Rust
87+
unsafe fn move_val_init_from_closure<T, F: FnOnce() -> T>(p: *mut T, f: F)
88+
{
89+
let ptr = &move *p;
90+
mem::forget(*ptr);
91+
*ptr = f(); // if `f` panics, `*ptr` is not dropped.
92+
}
93+
```
94+
95+
An `&move x.y` borrow, unlike the other borrows, actually moves out of
96+
`x.y`. This applies to all borrows, including implicit reborrows. I think
97+
this would make implicit reborrows useless, but it is the consequence of
98+
the rules.
99+
100+
Of course, it is possible to borrow `&move` references as either `&` or
101+
`&mut`, and not possible to borrow `&` or `&mut` references as `&move`.
102+
103+
## DerefMove
104+
105+
This allows moving out of user-defined types.
106+
107+
Add a `DerefMove` trait:
108+
```Rust
109+
pub trait DerefMove: DerefMut + DerefPure {
110+
fn deref_move(&mut self) -> &move Self::Target;
111+
}
112+
```
113+
114+
The `DerefMove` trait can't be called directly, in the same manner
115+
as `Drop` and for exactly the same reason - otherwise, this
116+
would be possible:
117+
118+
```Rust
119+
fn example<T>(data: T) -> T {
120+
let b = Box::new(data);
121+
drop(b.deref_move());
122+
*b // would return dropped data
123+
}
124+
```
125+
126+
It is also restricted in the same manner as `Drop` with regards to
127+
implementations and dropck.
128+
129+
If a type implements `DerefMove`, then the move checker treats it
130+
as a tree:
131+
132+
x
133+
- *x
134+
135+
It is not possible to move out of the ordinary fields of such a
136+
type, similarly to types implementing `Drop`.
137+
138+
When such a type is dropped, `*x` (aka `x.deref_move()`) is dropped
139+
first if it was not moved from already, similarly to `Box` today. Then
140+
the normal destructor and the destructors of the fields are called.
141+
142+
This means that `Vec<T>` can be implemented as
143+
144+
```Rust
145+
pub struct Vec<T> {
146+
buf: RawVec<T>,
147+
len: usize,
148+
}
149+
150+
impl<T> ops::Deref for Vec<T> {
151+
type Target = [T];
152+
153+
fn deref(&self) -> &[T] {
154+
unsafe {
155+
let p = self.buf.ptr();
156+
assume(!p.is_null());
157+
slice::from_raw_parts(p, self.len)
158+
}
159+
}
160+
}
161+
162+
impl<T> ops::DerefMut for Vec<T> {
163+
/* id. */
164+
}
165+
166+
impl<T> ops::DerefMove for Vec<T> {
167+
#[unsafe_destructor_blind_to_params]
168+
fn deref_move(&mut self) -> &move [T] {
169+
unsafe {
170+
let p = self.buf.ptr();
171+
assume(!p.is_null());
172+
slice::from_raw_parts_move(p, self.len)
173+
}
174+
}
175+
}
176+
177+
unsafe impl<T> ops::DerefPure for Vec<T> {}
178+
179+
// no `Drop` impl is needed - `RawVec` handles
180+
// that
181+
```
182+
183+
# Drawbacks
184+
[drawbacks]: #drawbacks
185+
186+
The new mutability kind adds a significant amount of complexity to the
187+
middle of the user-visible type-system. I think the move checker already
188+
supports most of that complexity, but there probably will be unexpected
189+
problems.
190+
191+
There may be some way to have the entire thing safe. However, all proposals
192+
that I have seen were very complicated.
193+
194+
# Alternatives
195+
[alternatives]: #alternatives
196+
197+
We may want to relax the [RFC1214] rules to allow `&'static move T` as an
198+
equivalent to `Unique<T>`.
199+
200+
Add more features of the move checker to the type-system, e.g. strongly
201+
linear `&out`. That is quite complex, and requires more considerations
202+
wrt. panics.
203+
204+
# Unresolved questions
205+
[unresolved]: #unresolved-questions
206+
207+
How to formalize the requirements for `DerefPure`?
208+
209+
Are there any issues with implementing `&move` lvalues "just like other lvalues"?
210+
211+
How do we do exhaustiveness checking on `box` patterns if there are also
212+
normal patterns? For example, how do we discover that the box pattern is
213+
useless here:
214+
215+
```Rust
216+
match x: Rc<Option<_>> {
217+
Rc { .. } => {}
218+
box None => {},
219+
}
220+
```
221+
222+
[RFC1214](https://github.com/rust-lang/rfcs/blob/master/text/1214-projections-lifetimes-and-wf.md)

0 commit comments

Comments
 (0)