rust - 如何在第一个 None 值上停止无限迭代器?

标签 rust iterator

我不知道为什么在下面的代码中,迭代在第一次遇到 None 时没有停止(在到达空范围 0..0 之后)

fn main() {
    let iter = (0..)
        .flat_map(move |i| if i < 10 { (0..i).into_iter() } else { 0..0 })
        .fuse();
    for i in iter {
        println!("{}", i);
    }
}

Playground报告说它必须被杀死,它为什么不停止?如何让它停止?

最佳答案

flat_map() 是执行此任务的错误工具。它旨在扁平化迭代器的迭代器,即每当它在内部迭代器上遇到 None 时,它的工作就是切换到下一个迭代器。如果你一直给它提供空的迭代器,它会一直循环直到找到一个非空的迭代器来传递值。 flat_map() 只会在 outer 迭代器终止迭代时终止迭代,在您的情况下永远不会发生。

您需要做两件事:首先,以不同于空迭代器的其他方式标记迭代结束 - 例如通过使用 Option,其中 Some 表示“这是给你的另一个迭代器”,None 表示“我不再对迭代感兴趣,你可以终止”。其次,您需要在扁平化步骤之前终止迭代,使用具有此功能的适配器,例如 take_while()scan() .

这是对您的代码的修改,它在 i 达到 10 后终止:

fn main() {
    let iter = (0..)
        .map(move |i| if i < 10 { Some(0..i) } else { None })
        .scan((), |_, item| item)
        .flatten();
    for i in iter {
        println!("{}", i);
    }
}

Playground

这里 flat_map() 分为三个部分:首先是一个 map(),它在我们想要的时候生成 Some(inner_iterator)迭代,当我们不再这样做时,None。然后是 scan(),它将迭代器转换为 next() 仅返回闭包返回的值的迭代器。由于闭包返回 item(包含在选项中的内部迭代器)不变,Some(inner_iterator) 将通过,而 None 将传播为迭代结束信号。最后,flatten() 将内部迭代器生成的项目集中到一个迭代器中,就像 flat_map() 在您的原始代码中所做的那样。

如果你真的想在第一个空的内部迭代器上终止,也可以通过提取 scan() 中的第一个值并测试它来安排:

// using 1.. to avoid immediately terminating on 0..0
let iter = (1..)
    .map(move |i| if i < 10 { 0..i } else { 0..0 })
    .scan((), |_, mut inner| match inner.next() {
        Some(first) => Some(once(first).chain(inner)),
        None => None,
    })
    .flatten();

Playground

请注意,fuse() 不是您想要的。它的目的是让迭代器在耗尽后能够被安全地查询。通常,如果您在 next() 已经返回 None 之后调用它,迭代器可能会崩溃。 fuse() 扩展了迭代器契约以允许在它已经返回 None 之后调用 next()。这是通过沿内部迭代器的单独标志实现的,并在 next() 中检查该标志。如果您在迭代器返回 None 后不查询迭代器(for 循环不会),那么您就不需要 fuse().

关于rust - 如何在第一个 None 值上停止无限迭代器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67686711/

相关文章:

regex - RE2 (Rust) 正则表达式不能按预期工作

java - 无法在 Java 中的 Iterable 实现中转换类型

c++ - 如果我的容器介于两个现有值之间,那么捏造迭代器类别是否合理?

c++ - C++中迭代器的重要性

pointers - () -> &str 函数的不可理解的行为

rust - 元组结构构造函数提示私有(private)字段

arrays - 如何在Rust中不同大小的数组之间复制?

java - Java 中的 LinkedList 是否实现了默认迭代器?

c++ - 返回迭代器似乎使它无效

rust - 如何在借用不可变值的比赛中进行变异?