重用 iter 变量进行条件跳过、过滤等

Ser*_*nad 6 rust

我有一个迭代器,我想在最终调用之前有条件地应用过滤器、跳过等collect()

用不同的语言,我可能会写类似的东西

// (Rust-like pseudocode)
let mut iter = [1,2,3,4].iter();
if should_filter {
    iter = iter.filter(|x| x % 2 == 0);
}
if should_truncate {
    iter = iter.take(2);
}
iter.collect()
Run Code Online (Sandbox Code Playgroud)

但由于iter是 类型Iter, 并且skip()返回filter()类型Skip, Filter, 我无法重用 的原始绑定iter。结果,我的 Rust 代码目前看起来像这样:

// (Rust-like pseudocode)
let mut iter = [1,2,3,4].iter();
if should_filter {
    iter = iter.filter(|x| x % 2 == 0);
}
if should_truncate {
    iter = iter.take(2);
}
iter.collect()
Run Code Online (Sandbox Code Playgroud)

有什么办法可以避免这种重复吗?

use*_*968 7

在这种情况下,您可以可靠地信任编译器的优化循环不变量。由于should_filter迭代时无法更改,编译器将发现它可以在循环之前检查前提条件并跳过测试 if should_filteris true。这意味着您可以简单地将条件放入循环中(这似乎效率低下)并且拥有更清晰的代码。即使检查没有从循环体中删除,CPU 的分支预测器也会轻松跳过它。同样,您可以“内联”should_truncate条件:

fn do_stuff(
    inp: impl IntoIterator<Item = u32>,
    should_filter: bool,
    should_truncate: bool,
) -> Vec<u32> {
    inp.into_iter()
        .filter(|x| should_filter && x % 2 == 0)
        .take(if should_truncate { 2 } else { usize::MAX })
        .collect()
}
Run Code Online (Sandbox Code Playgroud)


Jmb*_*Jmb 4

最简单的解决方案,也可能最接近您的“其他语言”在幕后所做的事情,就是对迭代器进行装箱:

let mut iter: Box<dyn Iterator<Item = &i32>> = Box::new ([1, 2, 3, 4].iter());
if should_filter {
    iter = Box::new (iter.filter(|x| *x % 2 == 0));
}
if should_truncate {
    iter = Box::new (iter.take(2));
}
let v: Vec<_> = iter.collect();
Run Code Online (Sandbox Code Playgroud)

操场