在其 for 循环中访问迭代器

Access iterator inside its for loop

我正在用 Rust 测试自定义双向迭代器,但我 运行 出错了

error[E0382]: borrow of moved value: `iter`
   --> src/main.rs:40:13
    |
37  |     let mut iter = BiIterator::from(vec![1, 2, 3]);
    |         -------- move occurs because `iter` has type `BiIterator<i32>`, which does not implement the `Copy` trait
38  |     for i in iter {
    |              ----
    |              |
    |              `iter` moved due to this implicit call to `.into_iter()`
    |              help: consider borrowing to avoid moving into the for loop: `&iter`
39  |         if i == 3 {
40  |             iter.position(0);
    |             ^^^^^^^^^^^^^^^^ value borrowed here after move
    |
note: this function takes ownership of the receiver `self`, which moves `iter`

reproducable code

for 循环正在获取迭代器的所有权。要在循环体内使用迭代器,您需要将 for 循环脱糖为 while let:

while let Some(i) = iter.next() {
    if i == 3 {
        iter.position(0);
    }
    println!("{}", i);
}

如果你想让你的迭代器在 for 循环中可用,你需要付出一些额外的努力。您可以为 &BiIterator 实现 Iterator,并为 pos 使用 interior mutability,因此 position() 可以采用 &self:

// don't need RefCell because we're just mutating a number
use std::cell::Cell;

struct BiIterator<T> {
    values: Vec<T>,
    pos: Cell<usize>,
}

impl<T: Clone> Iterator for &BiIterator<T> {
    type Item = T;
    fn next(&mut self) -> Option<Self::Item> {
        self.pos.set(self.pos.get() + 1);
        self.values.get(self.pos.get() - 1).cloned()
    }
}

impl<T> BiIterator<T> {
    pub fn position(&self, new_pos: usize) {
        self.pos.set(new_pos);
    }
    pub fn prev(&mut self) {
        self.pos.set(self.pos.get() - 1);
    }
}

impl<T> std::convert::From<Vec<T>> for BiIterator<T> {
    fn from(input: Vec<T>) -> Self {
        Self {
            values: input,
            pos: Cell::new(0),
        }
    }
}

通过这些更改,您最终可以按照编译器的原始建议使用 for i in &iter

fn main() {
    let iter = BiIterator::from(vec![1, 2, 3]);
    for i in &iter {
        if i == 3 {
            iter.position(0);
        }
        println!("{}", i);
    }
}

上面实现了一些额外的变化:

  • 不需要 Copy 绑定到 T,因为您只是在克隆它。任何 CopyT 都会自动 Clone,克隆它可以预期只执行廉价复制。
  • 界限是;将它们放在 impl 上。
  • 将 if/else if let/else 链替换为 match 或者更好的是 Option::cloned().