Skip to content

Commit bd12372

Browse files
committed
---
yaml --- r: 236111 b: refs/heads/stable c: 36a8b94 h: refs/heads/master i: 236109: 71a86c7 236107: 41eee3b 236103: 8561b47 236095: 23a57a0 v: v3
1 parent 047c620 commit bd12372

File tree

2 files changed

+195
-54
lines changed

2 files changed

+195
-54
lines changed

[refs]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ refs/heads/tmp: afae2ff723393b3ab4ccffef6ac7c6d1809e2da0
2929
refs/tags/1.0.0-alpha.2: 4c705f6bc559886632d3871b04f58aab093bfa2f
3030
refs/tags/homu-tmp: f859507de8c410b648d934d8f5ec1c52daac971d
3131
refs/tags/1.0.0-beta: 8cbb92b53468ee2b0c2d3eeb8567005953d40828
32-
refs/heads/stable: f54c5ad5660e81972b772be1c8852e1ef2969f28
32+
refs/heads/stable: 36a8b94464dd0cc7763fe3fb2fe9a3fbed273d06
3333
refs/tags/1.0.0: 55bd4f8ff2b323f317ae89e254ce87162d52a375
3434
refs/tags/1.1.0: bc3c16f09287e5545c1d3f76b7abd54f2eca868b
3535
refs/tags/1.2.0: f557861f822c34f07270347b94b5280de20a597e
Lines changed: 194 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
% Splitting Lifetimes
22

33
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:
88

99
```rust
1010
struct Foo {
@@ -49,11 +49,11 @@ container types like a tree, especially if distinct keys actually *do* map
4949
to the same value.
5050

5151
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:
5757

5858
```rust,ignore
5959
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]) {
6666
}
6767
```
6868

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

7373
However more subtle is how iterators that yield mutable references work.
7474
The iterator trait is defined as follows:
@@ -81,60 +81,201 @@ trait Iterator {
8181
}
8282
```
8383

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

9495
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.
9798

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

101-
For instance, VecDeque's IterMut:
102+
For instance here's a singly linked list:
102103

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+
}
109123
}
110124

111125
impl<'a, T> Iterator for IterMut<'a, T> {
112126
type Item = &'a mut T;
113127

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+
}
117260
}
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+
}
130272
}
131273
}
132274
}
133275
```
134276

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

Comments
 (0)