mirror of https://github.com/rust-lang/nomicon
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
292 lines
8.2 KiB
292 lines
8.2 KiB
8 years ago
|
# Splitting Borrows
|
||
10 years ago
|
|
||
|
The mutual exclusion property of mutable references can be very limiting when
|
||
9 years ago
|
working with a composite structure. The borrow checker understands some basic
|
||
9 years ago
|
stuff, but will fall over pretty easily. It does understand structs
|
||
9 years ago
|
sufficiently to know that it's possible to borrow disjoint fields of a struct
|
||
|
simultaneously. So this works today:
|
||
10 years ago
|
|
||
|
```rust
|
||
|
struct Foo {
|
||
|
a: i32,
|
||
|
b: i32,
|
||
|
c: i32,
|
||
|
}
|
||
|
|
||
|
let mut x = Foo {a: 0, b: 0, c: 0};
|
||
|
let a = &mut x.a;
|
||
|
let b = &mut x.b;
|
||
|
let c = &x.c;
|
||
|
*b += 1;
|
||
|
let c2 = &x.c;
|
||
|
*a += 10;
|
||
|
println!("{} {} {} {}", a, b, c, c2);
|
||
|
```
|
||
|
|
||
|
However borrowck doesn't understand arrays or slices in any way, so this doesn't
|
||
|
work:
|
||
|
|
||
|
```rust,ignore
|
||
9 years ago
|
let mut x = [1, 2, 3];
|
||
10 years ago
|
let a = &mut x[0];
|
||
|
let b = &mut x[1];
|
||
|
println!("{} {}", a, b);
|
||
|
```
|
||
|
|
||
|
```text
|
||
9 years ago
|
<anon>:4:14: 4:18 error: cannot borrow `x[..]` as mutable more than once at a time
|
||
|
<anon>:4 let b = &mut x[1];
|
||
|
^~~~
|
||
|
<anon>:3:14: 3:18 note: previous borrow of `x[..]` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `x[..]` until the borrow ends
|
||
|
<anon>:3 let a = &mut x[0];
|
||
|
^~~~
|
||
|
<anon>:6:2: 6:2 note: previous borrow ends here
|
||
|
<anon>:1 fn main() {
|
||
|
<anon>:2 let mut x = [1, 2, 3];
|
||
|
<anon>:3 let a = &mut x[0];
|
||
|
<anon>:4 let b = &mut x[1];
|
||
|
<anon>:5 println!("{} {}", a, b);
|
||
|
<anon>:6 }
|
||
|
^
|
||
10 years ago
|
error: aborting due to 2 previous errors
|
||
|
```
|
||
|
|
||
|
While it was plausible that borrowck could understand this simple case, it's
|
||
|
pretty clearly hopeless for borrowck to understand disjointness in general
|
||
|
container types like a tree, especially if distinct keys actually *do* map
|
||
|
to the same value.
|
||
|
|
||
|
In order to "teach" borrowck that what we're doing is ok, we need to drop down
|
||
9 years ago
|
to unsafe code. For instance, mutable slices expose a `split_at_mut` function
|
||
9 years ago
|
that consumes the slice and returns two mutable slices. One for everything to
|
||
9 years ago
|
the left of the index, and one for everything to the right. Intuitively we know
|
||
9 years ago
|
this is safe because the slices don't overlap, and therefore alias. However
|
||
|
the implementation requires some unsafety:
|
||
10 years ago
|
|
||
|
```rust,ignore
|
||
|
fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) {
|
||
9 years ago
|
let len = self.len();
|
||
|
let ptr = self.as_mut_ptr();
|
||
|
assert!(mid <= len);
|
||
10 years ago
|
unsafe {
|
||
9 years ago
|
(from_raw_parts_mut(ptr, mid),
|
||
|
from_raw_parts_mut(ptr.offset(mid as isize), len - mid))
|
||
10 years ago
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
9 years ago
|
This is actually a bit subtle. So as to avoid ever making two `&mut`'s to the
|
||
|
same value, we explicitly construct brand-new slices through raw pointers.
|
||
10 years ago
|
|
||
|
However more subtle is how iterators that yield mutable references work.
|
||
|
The iterator trait is defined as follows:
|
||
|
|
||
|
```rust
|
||
|
trait Iterator {
|
||
|
type Item;
|
||
|
|
||
|
fn next(&mut self) -> Option<Self::Item>;
|
||
|
}
|
||
|
```
|
||
|
|
||
9 years ago
|
Given this definition, Self::Item has *no* connection to `self`. This means that
|
||
|
we can call `next` several times in a row, and hold onto all the results
|
||
|
*concurrently*. This is perfectly fine for by-value iterators, which have
|
||
|
exactly these semantics. It's also actually fine for shared references, as they
|
||
|
admit arbitrarily many references to the same thing (although the iterator needs
|
||
|
to be a separate object from the thing being shared).
|
||
|
|
||
|
But mutable references make this a mess. At first glance, they might seem
|
||
|
completely incompatible with this API, as it would produce multiple mutable
|
||
|
references to the same object!
|
||
10 years ago
|
|
||
|
However it actually *does* work, exactly because iterators are one-shot objects.
|
||
9 years ago
|
Everything an IterMut yields will be yielded at most once, so we don't
|
||
|
actually ever yield multiple mutable references to the same piece of data.
|
||
10 years ago
|
|
||
9 years ago
|
Perhaps surprisingly, mutable iterators don't require unsafe code to be
|
||
9 years ago
|
implemented for many types!
|
||
10 years ago
|
|
||
9 years ago
|
For instance here's a singly linked list:
|
||
10 years ago
|
|
||
9 years ago
|
```rust
|
||
|
# fn main() {}
|
||
|
type Link<T> = Option<Box<Node<T>>>;
|
||
|
|
||
|
struct Node<T> {
|
||
|
elem: T,
|
||
|
next: Link<T>,
|
||
|
}
|
||
|
|
||
|
pub struct LinkedList<T> {
|
||
|
head: Link<T>,
|
||
|
}
|
||
|
|
||
|
pub struct IterMut<'a, T: 'a>(Option<&'a mut Node<T>>);
|
||
|
|
||
|
impl<T> LinkedList<T> {
|
||
|
fn iter_mut(&mut self) -> IterMut<T> {
|
||
|
IterMut(self.head.as_mut().map(|node| &mut **node))
|
||
|
}
|
||
10 years ago
|
}
|
||
|
|
||
|
impl<'a, T> Iterator for IterMut<'a, T> {
|
||
|
type Item = &'a mut T;
|
||
|
|
||
9 years ago
|
fn next(&mut self) -> Option<Self::Item> {
|
||
|
self.0.take().map(|node| {
|
||
|
self.0 = node.next.as_mut().map(|node| &mut **node);
|
||
|
&mut node.elem
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
Here's a mutable slice:
|
||
|
|
||
|
```rust
|
||
9 years ago
|
# fn main() {}
|
||
9 years ago
|
use std::mem;
|
||
|
|
||
|
pub struct IterMut<'a, T: 'a>(&'a mut[T]);
|
||
|
|
||
|
impl<'a, T> Iterator for IterMut<'a, T> {
|
||
|
type Item = &'a mut T;
|
||
|
|
||
|
fn next(&mut self) -> Option<Self::Item> {
|
||
|
let slice = mem::replace(&mut self.0, &mut []);
|
||
|
if slice.is_empty() { return None; }
|
||
|
|
||
|
let (l, r) = slice.split_at_mut(1);
|
||
|
self.0 = r;
|
||
|
l.get_mut(0)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl<'a, T> DoubleEndedIterator for IterMut<'a, T> {
|
||
|
fn next_back(&mut self) -> Option<Self::Item> {
|
||
|
let slice = mem::replace(&mut self.0, &mut []);
|
||
|
if slice.is_empty() { return None; }
|
||
|
|
||
|
let new_len = slice.len() - 1;
|
||
|
let (l, r) = slice.split_at_mut(new_len);
|
||
|
self.0 = l;
|
||
|
r.get_mut(0)
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
And here's a binary tree:
|
||
|
|
||
|
```rust
|
||
9 years ago
|
# fn main() {}
|
||
9 years ago
|
use std::collections::VecDeque;
|
||
|
|
||
|
type Link<T> = Option<Box<Node<T>>>;
|
||
|
|
||
|
struct Node<T> {
|
||
|
elem: T,
|
||
|
left: Link<T>,
|
||
|
right: Link<T>,
|
||
|
}
|
||
|
|
||
|
pub struct Tree<T> {
|
||
|
root: Link<T>,
|
||
|
}
|
||
|
|
||
|
struct NodeIterMut<'a, T: 'a> {
|
||
|
elem: Option<&'a mut T>,
|
||
|
left: Option<&'a mut Node<T>>,
|
||
|
right: Option<&'a mut Node<T>>,
|
||
|
}
|
||
|
|
||
|
enum State<'a, T: 'a> {
|
||
|
Elem(&'a mut T),
|
||
|
Node(&'a mut Node<T>),
|
||
|
}
|
||
|
|
||
|
pub struct IterMut<'a, T: 'a>(VecDeque<NodeIterMut<'a, T>>);
|
||
|
|
||
|
impl<T> Tree<T> {
|
||
|
pub fn iter_mut(&mut self) -> IterMut<T> {
|
||
|
let mut deque = VecDeque::new();
|
||
|
self.root.as_mut().map(|root| deque.push_front(root.iter_mut()));
|
||
|
IterMut(deque)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl<T> Node<T> {
|
||
|
pub fn iter_mut(&mut self) -> NodeIterMut<T> {
|
||
|
NodeIterMut {
|
||
|
elem: Some(&mut self.elem),
|
||
|
left: self.left.as_mut().map(|node| &mut **node),
|
||
|
right: self.right.as_mut().map(|node| &mut **node),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
impl<'a, T> Iterator for NodeIterMut<'a, T> {
|
||
|
type Item = State<'a, T>;
|
||
|
|
||
|
fn next(&mut self) -> Option<Self::Item> {
|
||
|
match self.left.take() {
|
||
|
Some(node) => Some(State::Node(node)),
|
||
|
None => match self.elem.take() {
|
||
|
Some(elem) => Some(State::Elem(elem)),
|
||
|
None => match self.right.take() {
|
||
|
Some(node) => Some(State::Node(node)),
|
||
|
None => None,
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl<'a, T> DoubleEndedIterator for NodeIterMut<'a, T> {
|
||
|
fn next_back(&mut self) -> Option<Self::Item> {
|
||
|
match self.right.take() {
|
||
|
Some(node) => Some(State::Node(node)),
|
||
|
None => match self.elem.take() {
|
||
|
Some(elem) => Some(State::Elem(elem)),
|
||
|
None => match self.left.take() {
|
||
|
Some(node) => Some(State::Node(node)),
|
||
|
None => None,
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl<'a, T> Iterator for IterMut<'a, T> {
|
||
|
type Item = &'a mut T;
|
||
|
fn next(&mut self) -> Option<Self::Item> {
|
||
|
loop {
|
||
|
match self.0.front_mut().and_then(|node_it| node_it.next()) {
|
||
|
Some(State::Elem(elem)) => return Some(elem),
|
||
|
Some(State::Node(node)) => self.0.push_front(node.iter_mut()),
|
||
|
None => if let None = self.0.pop_front() { return None },
|
||
|
}
|
||
10 years ago
|
}
|
||
9 years ago
|
}
|
||
|
}
|
||
|
|
||
|
impl<'a, T> DoubleEndedIterator for IterMut<'a, T> {
|
||
9 years ago
|
fn next_back(&mut self) -> Option<Self::Item> {
|
||
9 years ago
|
loop {
|
||
|
match self.0.back_mut().and_then(|node_it| node_it.next_back()) {
|
||
|
Some(State::Elem(elem)) => return Some(elem),
|
||
|
Some(State::Node(node)) => self.0.push_back(node.iter_mut()),
|
||
|
None => if let None = self.0.pop_back() { return None },
|
||
|
}
|
||
10 years ago
|
}
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
9 years ago
|
All of these are completely safe and work on stable Rust! This ultimately
|
||
|
falls out of the simple struct case we saw before: Rust understands that you
|
||
|
can safely split a mutable reference into subfields. We can then encode
|
||
|
permanently consuming a reference via Options (or in the case of slices,
|
||
|
replacing with an empty slice).
|