在其for循环内部访问迭代器

4

我正在测试 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`


您的链接没有提供片段。抛开这个不谈,编译器似乎提供了您所需的所有信息。 - Masklinn
为了在 Playground 上发布代码链接,您需要点击“分享”按钮。否则,您将得到一个指向空白 Playground 的链接... - Jmb
添加了嵌入式代码 - muppi090909
@Masklinn 我该如何解决这个问题? - muppi090909
1
@Masklinn 编译器正确地识别了问题,但没有提供修复它的提示。它提供的建议使用 for i in &iter 是无用的,因为它不能编译(因为 &iter 不可迭代),并且无法在循环内部调用 position()。对于 Rust 经验丰富的人来说,正确的解决方案是显而易见的,但对于初学者来说却不是那么简单。 - user4815162342
1个回答

7

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

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

如果你想让你的迭代器可以在for循环中使用,你需要额外投入一些努力。你可以为&BiIterator实现Iterator,并对pos使用内部可变性,这样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);
    }
}

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

  • T 不需要 Copy,因为你只是在克隆它。任何 T 都会自动 Clone,并且可以预期克隆它只会执行廉价的复制。
  • 结构体 几乎不需要边界;只需将其放在 impl 上。
  • match 或者更好的是 Option::cloned() 替换 if/else if let/else 链。

1
附注:虽然 Copy: Clone,其中 vCopyv.clone() 不一定等同于复制。虽然不鼓励不同的行为,但编译可能需要更多时间,因为它需要将 clone() 优化为复制,甚至可能无法成功。 - Chayim Friedman
2
@ChayimFriedman 好观点!我已经调整了最后一段的措辞,以反映这种可能性(虽然很小)。至于第二部分,我完全希望将clone()优化为复制,适用于所有派生自CloneCopy的类型。但无论如何,要求同时具有CopyClone可能没有意义 - 如果代码想要要求Copy以获得更好的优化,则应该使用*val而不是val.clone(),并省略Clone限定。 - user4815162342

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接