如何在Rust中正确地实现可迭代结构?

10

我正在尝试实现一个可以无限迭代的结构。可以将其类比为自然数。有一个限制:它不能实现Copy特性,因为该结构包含了一个String字段。

我还实现了Iterable特性和它唯一的成员函数fn next(&mut self) -> Option<Self::Item>

目前,我有以下代码来遍历我的结构的前10个项:

let mut counter = 0;
let mut game:Option<Game> = Game::new(&param);
loop {
    println!("{:?}", game); 

    game = g.next();
    counter = counter + 1;
    if counter > 10 { break; }
}

我希望让我的crate的用户能够使用for in结构遍历我的结构体,像这样:

for next_game in game {
  println!("{:?}", next_game);
} 

这有可能吗?我该如何实现?如何优化我的代码以及我该对我的结构体进行什么操作?

迭代器实现:

pub struct Game {
    /// The game hash
    pub hash: Vec<u8>
}

impl Iterator for Game {
    type Item = Game;

    fn next(&mut self) -> Option<Self::Item> {
        let mut hasher = Sha256::new();
        hasher.input(&hex::encode(&self.hash)); // we need to convert the hash into string first
        let result = hasher.result().to_vec();

        Some(Game {
            hash: result
        })
    }
}

示例:使用 for 出现错误行为

let mut game:Game = Game::new(&s).unwrap();
for g in game.take(2) {
    println!("{}", g);
}

现在如果我们运行示例,将会得到两个具有相同hashGame结构体,然而预期的行为是第一个g将具有等于SHA256(game.hash)的hash,而下一个g的哈希将会是SHA256(SHA256(game.hash))。当我调用.next()时它可以正常工作。


大多数人所做的是创建一个新的结构体,比如 GameIter,并将其提供给用户,例如 game.iter()。 任何实现了 Iterator 接口的结构体都可以在 for ... in ... 表达式中使用,如果你想限制迭代次数,只需使用 take - Caio
1
@AndreyTyukin:关于next函数... - Matthieu M.
1
实现 IteratorIntoIter 有什么问题吗? - Matthieu M.
2
相关:编写迭代器?如何为简单结构实现Iterator和IntoIterator? 答案基本上在问题标题中... - Andrey Tyukin
所有这些问题都是由于我实现的迭代器在 loopfor 循环中表现不同而导致的挫败感。我的上面使用 loop 的示例的工作方式与 for 的描述行为非常相似,而我无法获得预期的行为并且无法捕捉到问题所在,因此我认为问题出在我如何实现迭代器特质上。 - Vladimir Ignatev
显示剩余2条评论
1个回答

10
在Rust中,迭代器实际上可以分为两类。拥有结构体的迭代器可以使用.into_iter()创建,它会消耗self
而迭代结构体而不消耗它的迭代器可以通过.iter.iter_mut()来创建。
更多信息请参见相关问题:iter和into_iter之间的区别是什么?以及文档:三种迭代方式
要创建迭代器,您应该实现IntoIterator trait,将您的结构体转换为迭代器,或编写函数来创建迭代器:iter_mutiter
因此,按照惯例,您需要两个新类型IterMutIter
impl Iterator for Iter {
    type Item = /* ... */;
    fn next(&mut self) -> Option<Self::Item> {
        /* ... */
    }
}

impl Iterator for IterMut {
    type Item = &mut /* ... */;
    fn next(&mut self) -> Option<Self::Item> {
        /* ... */
    }
}

它们通常包含对父结构的引用。例如,对于链表,它可以是当前节点(在每次迭代中更新)。对于类似数组的结构,它可以是索引和对父结构的引用,因此每次都会增加索引并使用索引运算符访问元素等。


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