【问题标题】:Infinite loop when implementing custom iterator in Rust在 Rust 中实现自定义迭代器时的无限循环
【发布时间】:2016-11-28 17:26:03
【问题描述】:

我正在尝试实现一个列表zipper。到目前为止,我有:

#[derive(RustcDecodable, RustcEncodable, Debug, Clone)]
pub struct ListZipper {
    pub focus: Option<Tile>,
    pub left: VecDeque<Tile>,
    pub right: VecDeque<Tile>,
}

impl PartialEq for ListZipper {
    fn eq(&self, other: &ListZipper) -> bool {
        self.left == other.left && self.focus == other.focus && self.right == other.right
    }
}

我现在正在尝试实现一个迭代器

impl Iterator for ListZipper {
    type Item = Tile;

    fn next(&mut self) -> Option<Tile> {
        self.left.iter().chain(self.focus.iter()).chain(self.right.iter()).next().map(|w| *w)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
    }
}

在我看来这是有道理的。当迭代ListZipper 时,我想迭代left,然后是focus,然后是right。所以我链接这些迭代器并返回next()

如果ListZipper 中的所有字段都为空,这将正常工作。只要一个不是空的,迭代ListZipper 就会导致无限循环。

问题不在于链条。如果我将其替换为例如self.left.iter(),和left不为空,问题是一样的。 focusright 也是如此。

我尝试打印迭代器中的所有元素,它似乎从前到后通过VecDeque,然后卡住了。 IE。 next() 到达后面时光标不前进。

为什么?

我意识到我可能不希望 ListZipper 本身成为迭代器,但这是另一个讨论。

【问题讨论】:

  • 您确实意识到next 每次调用时都会创建一个全新的迭代器,对吗?

标签: iterator rust


【解决方案1】:

正如 cmets 中提到的,您的迭代器缺少一个关键的状态:它在迭代中走了多远。每次调用next,它都会完全从头构造另一个迭代器并获取第一个元素。

这是一个简化的例子:

struct ListZipper {
    focus: Option<u8>,
}

impl Iterator for ListZipper {
    type Item = u8;

    fn next(&mut self) -> Option<Self::Item> {
        self.focus.iter().next().cloned()
    }
}

fn main() {
    let lz = ListZipper { focus: Some(42) };
    let head: Vec<_> = lz.take(5).collect();
    println!("{:?}", head); // [42, 42, 42, 42, 42]
}

我意识到我可能不希望 ListZipper 本身成为一个迭代器,但这是另一个讨论。

不,真的不是^_^。您需要以某种方式改变正在迭代的事物,以便它可以更改并为每个后续调用具有不同的值。

如果您想返回现有迭代器和迭代器适配器的组合,请参阅Correct way to return an Iterator? 获取说明。

否则,您需要在调用 next 期间以某种方式更改 ListZipper

impl Iterator for ListZipper {
    type Item = Tile;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(v) = self.left.pop_front() {
            return Some(v);
        }

        if let Some(v) = self.focus.take() {
            return Some(v);
        }

        if let Some(v) = self.right.pop_front() {
            return Some(v);
        }

        None
    }
}

更简洁:

impl Iterator for ListZipper {
    type Item = Tile;

    fn next(&mut self) -> Option<Self::Item> {
        self.left.pop_front()
            .or_else(|| self.focus.take())
            .or_else(|| self.right.pop_front())
    }
}

请注意,您的 PartialEq 实现似乎与自动派生的相同...

use std::collections::VecDeque;

type Tile = u8;

#[derive(Debug, Clone, PartialEq)]
pub struct ListZipper {
    pub focus: Option<Tile>,
    pub left: VecDeque<Tile>,
    pub right: VecDeque<Tile>,
}

impl Iterator for ListZipper {
    type Item = Tile;

    fn next(&mut self) -> Option<Self::Item> {
        self.left.pop_front()
            .or_else(|| self.focus.take())
            .or_else(|| self.right.pop_front())
    }
}

fn main() {
    let lz = ListZipper {
        focus: Some(42),
        left: vec![1, 2, 3].into(),
        right: vec![97, 98, 99].into(),
    };

    let head: Vec<_> = lz.take(5).collect();

    println!("{:?}", head);
}

【讨论】:

  • 非常感谢您的详尽回答。现在很清楚了。也感谢您对PartialEq 的评论。
猜你喜欢
  • 2016-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-26
相关资源
最近更新 更多