Splitting Borrows

The mutual exclusion property of mutable references can be very limiting whenworking with a composite structure. The borrow checker understands some basicstuff, but will fall over pretty easily. It does understand structssufficiently to know that it’s possible to borrow disjoint fields of a structsimultaneously. So this works today:

  1. struct Foo {
  2. a: i32,
  3. b: i32,
  4. c: i32,
  5. }
  6. let mut x = Foo {a: 0, b: 0, c: 0};
  7. let a = &mut x.a;
  8. let b = &mut x.b;
  9. let c = &x.c;
  10. *b += 1;
  11. let c2 = &x.c;
  12. *a += 10;
  13. println!("{} {} {} {}", a, b, c, c2);

However borrowck doesn’t understand arrays or slices in any way, so this doesn’twork:

  1. let mut x = [1, 2, 3];
  2. let a = &mut x[0];
  3. let b = &mut x[1];
  4. println!("{} {}", a, b);
  1. error[E0499]: cannot borrow `x[..]` as mutable more than once at a time
  2. --> src/lib.rs:4:18
  3. |
  4. 3 | let a = &mut x[0];
  5. | ---- first mutable borrow occurs here
  6. 4 | let b = &mut x[1];
  7. | ^^^^ second mutable borrow occurs here
  8. 5 | println!("{} {}", a, b);
  9. 6 | }
  10. | - first borrow ends here
  11. error: aborting due to previous error

While it was plausible that borrowck could understand this simple case, it’spretty clearly hopeless for borrowck to understand disjointness in generalcontainer types like a tree, especially if distinct keys actually do mapto the same value.

In order to “teach” borrowck that what we’re doing is ok, we need to drop downto unsafe code. For instance, mutable slices expose a split_at_mut functionthat consumes the slice and returns two mutable slices. One for everything tothe left of the index, and one for everything to the right. Intuitively we knowthis is safe because the slices don’t overlap, and therefore alias. Howeverthe implementation requires some unsafety:

  1. # use std::slice::from_raw_parts_mut;
  2. # struct FakeSlice<T>(T);
  3. # impl<T> FakeSlice<T> {
  4. # fn len(&self) -> usize { unimplemented!() }
  5. # fn as_mut_ptr(&mut self) -> *mut T { unimplemented!() }
  6. fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) {
  7. let len = self.len();
  8. let ptr = self.as_mut_ptr();
  9. assert!(mid <= len);
  10. unsafe {
  11. (from_raw_parts_mut(ptr, mid),
  12. from_raw_parts_mut(ptr.offset(mid as isize), len - mid))
  13. }
  14. }
  15. # }

This is actually a bit subtle. So as to avoid ever making two &mut‘s to thesame value, we explicitly construct brand-new slices through raw pointers.

However more subtle is how iterators that yield mutable references work.The iterator trait is defined as follows:

  1. trait Iterator {
  2. type Item;
  3. fn next(&mut self) -> Option<Self::Item>;
  4. }

Given this definition, Self::Item has no connection to self. This means thatwe can call next several times in a row, and hold onto all the resultsconcurrently. This is perfectly fine for by-value iterators, which haveexactly these semantics. It’s also actually fine for shared references, as theyadmit arbitrarily many references to the same thing (although the iterator needsto be a separate object from the thing being shared).

But mutable references make this a mess. At first glance, they might seemcompletely incompatible with this API, as it would produce multiple mutablereferences to the same object!

However it actually does work, exactly because iterators are one-shot objects.Everything an IterMut yields will be yielded at most once, so we don’tactually ever yield multiple mutable references to the same piece of data.

Perhaps surprisingly, mutable iterators don’t require unsafe code to beimplemented for many types!

For instance here’s a singly linked list:

  1. # fn main() {}
  2. type Link<T> = Option<Box<Node<T>>>;
  3. struct Node<T> {
  4. elem: T,
  5. next: Link<T>,
  6. }
  7. pub struct LinkedList<T> {
  8. head: Link<T>,
  9. }
  10. pub struct IterMut<'a, T: 'a>(Option<&'a mut Node<T>>);
  11. impl<T> LinkedList<T> {
  12. fn iter_mut(&mut self) -> IterMut<T> {
  13. IterMut(self.head.as_mut().map(|node| &mut **node))
  14. }
  15. }
  16. impl<'a, T> Iterator for IterMut<'a, T> {
  17. type Item = &'a mut T;
  18. fn next(&mut self) -> Option<Self::Item> {
  19. self.0.take().map(|node| {
  20. self.0 = node.next.as_mut().map(|node| &mut **node);
  21. &mut node.elem
  22. })
  23. }
  24. }

Here’s a mutable slice:

  1. # fn main() {}
  2. use std::mem;
  3. pub struct IterMut<'a, T: 'a>(&'a mut[T]);
  4. impl<'a, T> Iterator for IterMut<'a, T> {
  5. type Item = &'a mut T;
  6. fn next(&mut self) -> Option<Self::Item> {
  7. let slice = mem::replace(&mut self.0, &mut []);
  8. if slice.is_empty() { return None; }
  9. let (l, r) = slice.split_at_mut(1);
  10. self.0 = r;
  11. l.get_mut(0)
  12. }
  13. }
  14. impl<'a, T> DoubleEndedIterator for IterMut<'a, T> {
  15. fn next_back(&mut self) -> Option<Self::Item> {
  16. let slice = mem::replace(&mut self.0, &mut []);
  17. if slice.is_empty() { return None; }
  18. let new_len = slice.len() - 1;
  19. let (l, r) = slice.split_at_mut(new_len);
  20. self.0 = l;
  21. r.get_mut(0)
  22. }
  23. }

And here’s a binary tree:

  1. # fn main() {}
  2. use std::collections::VecDeque;
  3. type Link<T> = Option<Box<Node<T>>>;
  4. struct Node<T> {
  5. elem: T,
  6. left: Link<T>,
  7. right: Link<T>,
  8. }
  9. pub struct Tree<T> {
  10. root: Link<T>,
  11. }
  12. struct NodeIterMut<'a, T: 'a> {
  13. elem: Option<&'a mut T>,
  14. left: Option<&'a mut Node<T>>,
  15. right: Option<&'a mut Node<T>>,
  16. }
  17. enum State<'a, T: 'a> {
  18. Elem(&'a mut T),
  19. Node(&'a mut Node<T>),
  20. }
  21. pub struct IterMut<'a, T: 'a>(VecDeque<NodeIterMut<'a, T>>);
  22. impl<T> Tree<T> {
  23. pub fn iter_mut(&mut self) -> IterMut<T> {
  24. let mut deque = VecDeque::new();
  25. self.root.as_mut().map(|root| deque.push_front(root.iter_mut()));
  26. IterMut(deque)
  27. }
  28. }
  29. impl<T> Node<T> {
  30. pub fn iter_mut(&mut self) -> NodeIterMut<T> {
  31. NodeIterMut {
  32. elem: Some(&mut self.elem),
  33. left: self.left.as_mut().map(|node| &mut **node),
  34. right: self.right.as_mut().map(|node| &mut **node),
  35. }
  36. }
  37. }
  38. impl<'a, T> Iterator for NodeIterMut<'a, T> {
  39. type Item = State<'a, T>;
  40. fn next(&mut self) -> Option<Self::Item> {
  41. match self.left.take() {
  42. Some(node) => Some(State::Node(node)),
  43. None => match self.elem.take() {
  44. Some(elem) => Some(State::Elem(elem)),
  45. None => match self.right.take() {
  46. Some(node) => Some(State::Node(node)),
  47. None => None,
  48. }
  49. }
  50. }
  51. }
  52. }
  53. impl<'a, T> DoubleEndedIterator for NodeIterMut<'a, T> {
  54. fn next_back(&mut self) -> Option<Self::Item> {
  55. match self.right.take() {
  56. Some(node) => Some(State::Node(node)),
  57. None => match self.elem.take() {
  58. Some(elem) => Some(State::Elem(elem)),
  59. None => match self.left.take() {
  60. Some(node) => Some(State::Node(node)),
  61. None => None,
  62. }
  63. }
  64. }
  65. }
  66. }
  67. impl<'a, T> Iterator for IterMut<'a, T> {
  68. type Item = &'a mut T;
  69. fn next(&mut self) -> Option<Self::Item> {
  70. loop {
  71. match self.0.front_mut().and_then(|node_it| node_it.next()) {
  72. Some(State::Elem(elem)) => return Some(elem),
  73. Some(State::Node(node)) => self.0.push_front(node.iter_mut()),
  74. None => if let None = self.0.pop_front() { return None },
  75. }
  76. }
  77. }
  78. }
  79. impl<'a, T> DoubleEndedIterator for IterMut<'a, T> {
  80. fn next_back(&mut self) -> Option<Self::Item> {
  81. loop {
  82. match self.0.back_mut().and_then(|node_it| node_it.next_back()) {
  83. Some(State::Elem(elem)) => return Some(elem),
  84. Some(State::Node(node)) => self.0.push_back(node.iter_mut()),
  85. None => if let None = self.0.pop_back() { return None },
  86. }
  87. }
  88. }
  89. }

All of these are completely safe and work on stable Rust! This ultimatelyfalls out of the simple struct case we saw before: Rust understands that youcan safely split a mutable reference into subfields. We can then encodepermanently consuming a reference via Options (or in the case of slices,replacing with an empty slice).