1
1
% Splitting Lifetimes
2
2
3
3
The mutual exclusion property of mutable references can be very limiting when
4
- working with a composite structure. The borrow checker understands some basic stuff, but
5
- will fall over pretty easily. It * does* understand structs sufficiently to
6
- know that it's possible to borrow disjoint fields of a struct simultaneously.
7
- So this works today:
4
+ working with a composite structure. The borrow checker understands some basic
5
+ stuff, but will fall over pretty easily. It * does* understand structs
6
+ sufficiently to know that it's possible to borrow disjoint fields of a struct
7
+ simultaneously. So this works today:
8
8
9
9
``` rust
10
10
struct Foo {
@@ -49,11 +49,11 @@ container types like a tree, especially if distinct keys actually *do* map
49
49
to the same value.
50
50
51
51
In order to "teach" borrowck that what we're doing is ok, we need to drop down
52
- to unsafe code. For instance, mutable slices expose a ` split_at_mut ` function that
53
- consumes the slice and returns * two* mutable slices. One for everything to the
54
- left of the index, and one for everything to the right. Intuitively we know this
55
- is safe because the slices don't alias. However the implementation requires some
56
- unsafety:
52
+ to unsafe code. For instance, mutable slices expose a ` split_at_mut ` function
53
+ that consumes the slice and returns * two* mutable slices. One for everything to
54
+ the left of the index, and one for everything to the right. Intuitively we know
55
+ this is safe because the slices don't alias. However the implementation requires
56
+ some unsafety:
57
57
58
58
``` rust,ignore
59
59
fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) {
@@ -66,9 +66,9 @@ fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) {
66
66
}
67
67
```
68
68
69
- This is pretty plainly dangerous. We use transmute to duplicate the slice with an
70
- * unbounded* lifetime, so that it can be treated as disjoint from the other until
71
- we unify them when we return.
69
+ This is pretty plainly dangerous. We use transmute to duplicate the slice with
70
+ an * unbounded* lifetime, so that it can be treated as disjoint from the other
71
+ until we unify them when we return.
72
72
73
73
However more subtle is how iterators that yield mutable references work.
74
74
The iterator trait is defined as follows:
@@ -81,60 +81,201 @@ trait Iterator {
81
81
}
82
82
```
83
83
84
- Given this definition, Self::Item has * no* connection to ` self ` . This means
85
- that we can call ` next ` several times in a row, and hold onto all the results
86
- * concurrently* . This is perfectly fine for by-value iterators, which have exactly
87
- these semantics. It's also actually fine for shared references, as they admit
88
- arbitrarily many references to the same thing (although the
89
- iterator needs to be a separate object from the thing being shared). But mutable
90
- references make this a mess. At first glance, they might seem completely
91
- incompatible with this API, as it would produce multiple mutable references to
92
- the same object!
84
+ Given this definition, Self::Item has * no* connection to ` self ` . This means that
85
+ we can call ` next ` several times in a row, and hold onto all the results
86
+ * concurrently* . This is perfectly fine for by-value iterators, which have
87
+ exactly these semantics. It's also actually fine for shared references, as they
88
+ admit arbitrarily many references to the same thing (although the iterator needs
89
+ to be a separate object from the thing being shared).
90
+
91
+ But mutable references make this a mess. At first glance, they might seem
92
+ completely incompatible with this API, as it would produce multiple mutable
93
+ references to the same object!
93
94
94
95
However it actually * does* work, exactly because iterators are one-shot objects.
95
- Everything an IterMut yields will be yielded * at most* once, so we don't * actually *
96
- ever yield multiple mutable references to the same piece of data.
96
+ Everything an IterMut yields will be yielded * at most* once, so we don't
97
+ * actually * ever yield multiple mutable references to the same piece of data.
97
98
98
- In general all mutable iterators require * some * unsafe code * somewhere * , though.
99
- Whether it's raw pointers, or safely composing on top of * another * IterMut.
99
+ Perhaps surprisingly, mutable iterators * don't * require unsafe code to be
100
+ implemented for many types!
100
101
101
- For instance, VecDeque 's IterMut :
102
+ For instance here 's a singly linked list :
102
103
103
- ``` rust,ignore
104
- struct IterMut<'a, T:'a> {
105
- // The whole backing array. Some of these indices are initialized!
106
- ring: &'a mut [T],
107
- tail: usize,
108
- head: usize,
104
+ ``` rust
105
+ # fn main () {}
106
+ type Link <T > = Option <Box <Node <T >>>;
107
+
108
+ struct Node <T > {
109
+ elem : T ,
110
+ next : Link <T >,
111
+ }
112
+
113
+ pub struct LinkedList <T > {
114
+ head : Link <T >,
115
+ }
116
+
117
+ pub struct IterMut <'a , T : 'a >(Option <& 'a mut Node <T >>);
118
+
119
+ impl <T > LinkedList <T > {
120
+ fn iter_mut (& mut self ) -> IterMut <T > {
121
+ IterMut (self . head. as_mut (). map (| node | & mut * * node ))
122
+ }
109
123
}
110
124
111
125
impl <'a , T > Iterator for IterMut <'a , T > {
112
126
type Item = & 'a mut T ;
113
127
114
- fn next(&mut self) -> Option<&'a mut T> {
115
- if self.tail == self.head {
116
- return None;
128
+ fn next (& mut self ) -> Option <Self :: Item > {
129
+ self . 0. take (). map (| node | {
130
+ self . 0 = node . next. as_mut (). map (| node | & mut * * node );
131
+ & mut node . elem
132
+ })
133
+ }
134
+ }
135
+ ```
136
+
137
+ Here's a mutable slice:
138
+
139
+ ``` rust
140
+ use std :: mem;
141
+
142
+ pub struct IterMut <'a , T : 'a >(& 'a mut [T ]);
143
+
144
+ impl <'a , T > Iterator for IterMut <'a , T > {
145
+ type Item = & 'a mut T ;
146
+
147
+ fn next (& mut self ) -> Option <Self :: Item > {
148
+ let slice = mem :: replace (& mut self . 0 , & mut []);
149
+ if slice . is_empty () { return None ; }
150
+
151
+ let (l , r ) = slice . split_at_mut (1 );
152
+ self . 0 = r ;
153
+ l . get_mut (0 )
154
+ }
155
+ }
156
+
157
+ impl <'a , T > DoubleEndedIterator for IterMut <'a , T > {
158
+ fn next_back (& mut self ) -> Option <Self :: Item > {
159
+ let slice = mem :: replace (& mut self . 0 , & mut []);
160
+ if slice . is_empty () { return None ; }
161
+
162
+ let new_len = slice . len () - 1 ;
163
+ let (l , r ) = slice . split_at_mut (new_len );
164
+ self . 0 = l ;
165
+ r . get_mut (0 )
166
+ }
167
+ }
168
+ ```
169
+
170
+ And here's a binary tree:
171
+
172
+ ``` rust
173
+ use std :: collections :: VecDeque ;
174
+
175
+ type Link <T > = Option <Box <Node <T >>>;
176
+
177
+ struct Node <T > {
178
+ elem : T ,
179
+ left : Link <T >,
180
+ right : Link <T >,
181
+ }
182
+
183
+ pub struct Tree <T > {
184
+ root : Link <T >,
185
+ }
186
+
187
+ struct NodeIterMut <'a , T : 'a > {
188
+ elem : Option <& 'a mut T >,
189
+ left : Option <& 'a mut Node <T >>,
190
+ right : Option <& 'a mut Node <T >>,
191
+ }
192
+
193
+ enum State <'a , T : 'a > {
194
+ Elem (& 'a mut T ),
195
+ Node (& 'a mut Node <T >),
196
+ }
197
+
198
+ pub struct IterMut <'a , T : 'a >(VecDeque <NodeIterMut <'a , T >>);
199
+
200
+ impl <T > Tree <T > {
201
+ pub fn iter_mut (& mut self ) -> IterMut <T > {
202
+ let mut deque = VecDeque :: new ();
203
+ self . root. as_mut (). map (| root | deque . push_front (root . iter_mut ()));
204
+ IterMut (deque )
205
+ }
206
+ }
207
+
208
+ impl <T > Node <T > {
209
+ pub fn iter_mut (& mut self ) -> NodeIterMut <T > {
210
+ NodeIterMut {
211
+ elem : Some (& mut self . elem),
212
+ left : self . left. as_mut (). map (| node | & mut * * node ),
213
+ right : self . right. as_mut (). map (| node | & mut * * node ),
214
+ }
215
+ }
216
+ }
217
+
218
+
219
+ impl <'a , T > Iterator for NodeIterMut <'a , T > {
220
+ type Item = State <'a , T >;
221
+
222
+ fn next (& mut self ) -> Option <Self :: Item > {
223
+ match self . left. take () {
224
+ Some (node ) => Some (State :: Node (node )),
225
+ None => match self . elem. take () {
226
+ Some (elem ) => Some (State :: Elem (elem )),
227
+ None => match self . right. take () {
228
+ Some (node ) => Some (State :: Node (node )),
229
+ None => None ,
230
+ }
231
+ }
232
+ }
233
+ }
234
+ }
235
+
236
+ impl <'a , T > DoubleEndedIterator for NodeIterMut <'a , T > {
237
+ fn next_back (& mut self ) -> Option <Self :: Item > {
238
+ match self . right. take () {
239
+ Some (node ) => Some (State :: Node (node )),
240
+ None => match self . elem. take () {
241
+ Some (elem ) => Some (State :: Elem (elem )),
242
+ None => match self . left. take () {
243
+ Some (node ) => Some (State :: Node (node )),
244
+ None => None ,
245
+ }
246
+ }
247
+ }
248
+ }
249
+ }
250
+
251
+ impl <'a , T > Iterator for IterMut <'a , T > {
252
+ type Item = & 'a mut T ;
253
+ fn next (& mut self ) -> Option <Self :: Item > {
254
+ loop {
255
+ match self . 0. front_mut (). and_then (| node_it | node_it . next ()) {
256
+ Some (State :: Elem (elem )) => return Some (elem ),
257
+ Some (State :: Node (node )) => self . 0. push_front (node . iter_mut ()),
258
+ None => if let None = self . 0. pop_front () { return None },
259
+ }
117
260
}
118
- let tail = self.tail;
119
- self.tail = wrap_index(self.tail.wrapping_add(1), self.ring.len());
120
-
121
- unsafe {
122
- // might as well do unchecked indexing since wrap_index has us
123
- // in-bounds, and many of the "middle" indices are uninitialized
124
- // anyway.
125
- let elem = self.ring.get_unchecked_mut(tail);
126
-
127
- // round-trip through a raw pointer to unbound the lifetime from
128
- // ourselves
129
- Some(&mut *(elem as *mut _))
261
+ }
262
+ }
263
+
264
+ impl <'a , T > DoubleEndedIterator for IterMut <'a , T > {
265
+ fn next (& mut self ) -> Option <Self :: Item > {
266
+ loop {
267
+ match self . 0. back_mut (). and_then (| node_it | node_it . next_back ()) {
268
+ Some (State :: Elem (elem )) => return Some (elem ),
269
+ Some (State :: Node (node )) => self . 0. push_back (node . iter_mut ()),
270
+ None => if let None = self . 0. pop_back () { return None },
271
+ }
130
272
}
131
273
}
132
274
}
133
275
```
134
276
135
- A very subtle but interesting detail in this design is that it * relies on
136
- privacy to be sound* . Borrowck works on some very simple rules. One of those rules
137
- is that if we have a live &mut Foo and Foo contains an &mut Bar, then that &mut
138
- Bar is * also* live. Since IterMut is always live when ` next ` can be called, if
139
- ` ring ` were public then we could mutate ` ring ` while outstanding mutable borrows
140
- to it exist!
277
+ All of these are completely safe and work on stable Rust! This ultimately
278
+ falls out of the simple struct case we saw before: Rust understands that you
279
+ can safely split a mutable reference into subfields. We can then encode
280
+ permanently consuming a reference via Options (or in the case of slices,
281
+ replacing with an empty slice).
0 commit comments