拆分 Borrows

在处理复合结构时,可变引用的互斥属性会有很大的限制。借用检查器理解一些基本的东西,但是很容易就会出现问题。它对结构有足够的了解,知道有可能同时借用一个结构中不相干的字段。所以现在这个方法是可行的:

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

然而 borrowck 完全不理解数组或 slice,所以这会挂:

  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

虽然 borrowck 能理解这个简单的案例是合理的,但对于 borrowck 来说,要理解像树这样的一般容器类型的不连通性显然是没有希望的,尤其是当不同的键确实映射到相同的值时。

为了“教导” borrowck 我们正在做的事情是正确的,我们需要使用到不安全的代码。例如,可变 slice 暴露了一个split_at_mut函数,它消耗这个 slice 并返回两个可变 slice。一个用于索引左边的所有内容,一个用于右边的所有内容。直观地讲,我们知道这是安全的,因为这些分片不会重叠,因此可以进行别名操作。然而,这个实现需要一些不安全代码:

  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. pub 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. unsafe {
  10. assert!(mid <= len);
  11. (from_raw_parts_mut(ptr, mid),
  12. from_raw_parts_mut(ptr.add(mid), len - mid))
  13. }
  14. }
  15. # }

这实际上是有点微妙的。为了避免对同一个值进行两次&mut,我们明确地通过原始指针构造全新的切片。

然而,更微妙的是产生可变引用的迭代器如何工作。迭代器 trait 定义如下:

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

考虑到这个定义,Self::Item 与self没有联系。这意味着我们可以连续多次调用next,并将所有的结果并发地保留下来。这对逐值迭代器来说是非常好的,因为它有这样的语义。这对共享引用来说也很好,因为它们允许对同一事物有任意多的引用(尽管迭代器需要和被共享的事物是一个独立的对象)。

但是可变的引用让这变得一团糟。乍一看,它们似乎与这个 API 完全不兼容,因为它将产生对同一个对象的多个可变引用!

然而它实际上有效的,正是因为迭代器是一次性的对象。IterMut 产生的所有东西最多只能产生一次,所以我们实际上不会产生对同一块数据的多个可变引用。

也许令人惊讶的是,对于许多类型,可变迭代器不需要实现不安全的代码。

例如,这里有一个单向链表:

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

下面是一个可变的 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. }

接着是一个二叉树:

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

所有这些都是完全安全的,并且可以在稳定的 Rust 上运行!这最终落在了我们之前看到的简单结构案例中。Rust 知道你可以安全地将一个可变的引用分割成子字段。然后我们可以通过 Options(或者在分片的情况下,用空分片替换)来消耗掉这个引用并进行编码。