|
| 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