【问题标题】:Access iterator inside its for loop在其 for 循环中访问迭代器
【发布时间】:2022-01-27 07:54:30
【问题描述】:

我正在 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

【问题讨论】:

  • 您的链接不提供sn-p。除此之外,编译器似乎提供了您需要的所有信息。
  • 要在操场上发布指向您的代码的链接,您需要单击“共享”按钮。否则你会得到一个空旷游乐场的链接……
  • 添加了嵌入代码
  • @Masklinn 我该如何解决这个问题?
  • @Masklinn 编译器正确识别问题,但没有提供如何修复它的提示。它提供的使用for i in &amp;iter 的建议没有用,因为它无法编译(因为&amp;iter 不可迭代),并且无论如何都不允许在循环内调用position()。对于有 Rust 经验的人来说,正确的解决方案是显而易见的,但对于初学者来说并非如此。

标签: for-loop rust iterator iteration move-semantics


【解决方案1】:

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

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

如果您想让您的迭代器在 for 循环中可用,您需要付出一些额外的努力。你可以为&amp;BiIterator实现Iterator,为pos使用interior mutability,所以position()可以取&amp;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 &amp;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,因为您只是在克隆它。任何TCopy 都会自动成为Clone,并且可以预期克隆它只是执行廉价复制。
  • 边界是almost never needed on the struct;把它们放在impl 上。
  • 将 if/else if let/else 链替换为 match,或者更好的是,替换为 Option::cloned()

【讨论】:

  • 旁注:Copy: Clone, v.clone() 其中vCopy 与复制不一定相同。虽然强烈反对不同的行为,但编译可能需要更多时间,因为它需要将clone() 优化为副本,甚至可能不会成功。
  • @ChayimFriedman 好点!我已经调整了最后一段的措辞以反映这种可能性(尽管可能很小)。至于第二部分,我完全希望将clone() 优化为一个副本,以适用于CloneCopy 派生的所有类型。但无论如何,需要both CopyClone 可能没有意义 - 如果代码需要Copy 以进行更好的优化,那么它应该只使用*val 而不是val.clone(),并省略Clone 绑定。
猜你喜欢
  • 1970-01-01
  • 2010-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-23
  • 2016-08-07
  • 1970-01-01
相关资源
最近更新 更多