与Rust编译器的斗争- 7

505次阅读  |  发布于1年以前

假设我们想在Rust中创建一个slice或vector的迭代器,迭代器从指定索引k处开始,而不是从起始索引0开始。这对于遍历环形缓冲区非常有用。

你能实现下面的iter_from()函数,使程序精确地打印出5,6,7,8,9,0,1,2,3,4吗?

fn iter_from<T>(slice: &[T], k: usize) -> impl Iterator<Item=&T> {
  // todo
}

fn main() {
  let array = [0,1,2,3,4,5,6,7,8,9];
  for i in iter_from(&array, 5) {
    print!("{},", i);
  } // 5,6,7,8,9,0,1,2,3,4,
}

我的解决方案是使用Iterator::chain方法:

fn iter_from<T>(slice: &[T], k: usize) -> impl Iterator<Item=&T> {
    slice[k..].iter().chain(slice[..k].iter())
}

现在,这是今天真正的挑战——你能实现函数的可变变体吗?

fn iter_mut_from<T>(slice: &mut [T], k: usize) -> impl Iterator<Item=&mut T> {
  // todo
}

有人可能会认为我们可以简单地将&替换为&mut,将iter替换为iter_mut,就像这样:

fn iter_mut_from<T>(slice: &mut [T], k: usize) -> impl Iterator<Item=&mut T> {
  slice[k..].iter_mut().chain(slice[..k].iter_mut())
}

然而,这行不通。编译器会报错:对同一切片有多个可变借用。

error[E0499]: cannot borrow `*slice` as mutable more than once at a time
 --> src/main.rs:6:33
  |
5 | fn iter_mut_from<T>(slice: &mut [T], k: usize) -> impl Iterator<Item=&mut T> {
  |                            - let's call the lifetime of this reference `'1`
6 |     slice[k..].iter_mut().chain(slice[..k].iter_mut())
  |     ----------------------------^^^^^-----------------
  |     |                           |
  |     |                           second mutable borrow occurs here
  |     first mutable borrow occurs here
  |     returning this value requires that `*slice` is borrowed for `'1`

For more information about this error, try `rustc --explain E0499`.

这是因为我们对同一个slice请求了两次可变借用,一次是slice[k..],另一次是slice[..k]。我们知道这两者是互斥的,但是编译器还不够聪明。

为了显式地告诉编译器我们在索引k处将切片拆分为两个互斥的子切片,我们需要使用内置方法split_at_mut(),它拆分切片并为每一侧返回可变借用:

pub fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T])

现在,我们可以实现iter_mut_from()方法:

fn iter_mut_from<T>(slice: &mut [T], k: usize) -> impl Iterator<Item=&mut T> {
  let (s1, s2) = slice.split_at_mut(k);
  s2.iter_mut().chain(s1.iter_mut())
}

我们已经学习了如何在Rust中创建自定义迭代器,它从给定的索引开始并循环回到开头。我们使用了chain()和split_at_mut()方法来实现这个功能。迭代器是在Rust中编写优雅高效代码的强大工具。

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8