在 Rust 中重用迭代器的最有效方法是什么?

What's the most efficient way to reuse an iterator in Rust?

我想重复使用我制作的迭代器,以避免从头开始重新创建它。但是迭代器似乎无法 clone 并且 collect 移动了迭代器所以我不能重用它。

这大致相当于我正在尝试做的事情。

let my_iter = my_string.unwrap_or("A").chars().flat_map(|c|c.to_uppercase()).map(|c| Tag::from(c).unwrap() );
let my_struct = {
  one: my_iter.collect(),
  two: my_iter.map(|c|{(c,Vec::new())}).collect(),
  three: my_iter.filter_map(|c|if c.predicate(){Some(c)}else{None}).collect(),
  four: my_iter.map(|c|{(c,1.0/my_float)}).collect(),
  five: my_iter.map(|c|(c,arg_time.unwrap_or(time::now()))).collect(),
  //etc...
}

您可以使用闭包来获得相同的迭代器:

#[derive(Debug)]
struct MyStruct{
    one:Vec<char>,
    two:Vec<char>,
    three:String
}

fn main() {
    let my_string:String = "ABCD1234absd".into();
    let my_iter = || my_string.chars();
    let my_struct = MyStruct{
        one: my_iter().collect(),
        two: my_iter().filter(|x| x.is_numeric()).collect(),
        three: my_iter().filter(|x| x.is_lowercase()).collect()
    };
    println!("{:?}", my_struct);
}

另请参阅此 Correct way to return an Iterator? 问题。

您也可以克隆迭代器(请参阅@Paolo Falabella 关于迭代器可克隆性的回答):

fn main() {
    let v = vec![1,2,3,4,5,6,7,8,9]; 
    let mut i = v.iter().skip(2);
    let mut j = i.clone();
    println!("{:?}", i.take(3).collect::<Vec<_>>());
    println!("{:?}", j.filter(|&x| x%2==0).collect::<Vec<_>>());
}

遗憾的是我不知道哪种方式更有效

您应该在优化某些东西之前进行概要分析,否则您最终可能会使事情 变得比他们需要的更慢和更复杂。

您示例中的迭代器

let my_iter = my_string.unwrap_or("A").chars().flat_map(|c|c.to_uppercase()).map(|c| Tag::from(c).unwrap() );

是在堆栈上分配的精简结构。克隆它们并不比从头构建它们便宜多少。

.chars().flat_map(|c| c.to_uppercase()) 构建一个迭代器只需要一纳秒,而我 benchmark 它。

根据相同的基准,将迭代器创建包装在闭包中比简单地就地构建迭代器需要更多时间。

克隆 Vec 迭代器并不比就地构建迭代器快多少,两者几乎都是即时的。

test construction_only    ... bench:           1 ns/iter (+/- 0)
test inplace_construction ... bench:         249 ns/iter (+/- 20)
test closure              ... bench:         282 ns/iter (+/- 18)
test vec_inplace_iter     ... bench:           0 ns/iter (+/- 0)
test vec_clone_iter       ... bench:           0 ns/iter (+/- 0)

如果迭代器的所有 "pieces" 都是 Clone-able,那么迭代器一般都是 Clone-able 的。 my_iter 中有几个不是:匿名闭包(如 flat_map 中的闭包)和 to_uppercase.[=22= 返回的 ToUppercase 结构]

您可以做的是:

  1. 重建整个东西(正如@ArtemGr 建议的那样)。您可以使用宏来避免重复。有点难看,但应该可以。
  2. 在填充 my_struct 之前将 my_iter 收集到 Vec 中(因为您似乎无论如何都将其收集在那里):let my_iter: Vec<char> = my_string.unwrap_or("A").chars().flat_map(|c|c.to_uppercase()).map(|c| Tag::from(c).unwrap() ).collect();
  3. 创建您自己的自定义迭代器。没有你对 my_string 的定义(因为你在上面调用了 unwrap_or 我认为它不是 String)和 Tag 很难更具体地帮助你。