rust - 在 Rust 中定义采用迭代器而不消耗的函数的惯用方法是什么?

标签 rust iterator

我一直在努力度过2020年Advent of Code在 Rust 中,试图产生涉及相当通用函数的解决方案,这些函数(理论上)可以在其他地方重用,而不必过多担心类型。第 2 天,问题涉及检查文件的每一行,以确保它是符合某些给定条件的字符串。我当前的方法是定义一个类似于以下的函数:

fn filter_lines<'a>(lines: impl IntoIterator<Item = &'a str>) -> Vec<&'a str> {
    lines
        .into_iter()
        .filter(|line| verify_line(line))
        .collect()
}

在这里,verify_line只是一些需要 &str 的函数并根据该行是否符合特定规范生成一个 bool 值。

问题是这个函数似乎必然消耗迭代器。我尝试按如下方式重写它以使用对 lines 的引用,并克隆lines在对其进行操作之前,但此代码无法编译:

fn filter_lines<'a>(lines: &impl IntoIterator<Item = &'a str>) -> Vec<&'a str> {
    let lines = lines.clone();
    lines
        .into_iter()
        .filter(|line| verify_line(line))
        .collect()
}

编译器产生以下错误:

error[E0507]: cannot move out of `*lines` which is behind a shared reference
  --> src/main.rs:40:5
   |
40 |     lines
   |     ^^^^^ move occurs because `*lines` has type `impl IntoIterator<Item = &'a str>`, which does not implement the `Copy` trait

我想我明白为什么不允许这样做,但我不确定如何惯用地定义一个函数,例如 filter_lines这不会消耗迭代器。那么,什么是更好的方法来实现这个filter_lines函数可以按如下方式调用?

let some_str: String = get_file_contents();
let lines = some_str.lines();
println!("Filtered lines: {:?}", filter_lines(&lines));
println!("All lines: {:?}", lines.collect::<Vec<&str>>());

最佳答案

The issue for me is that this function appears to necessarily consume the iterator.

这种情况在任何情况下都会发生,因为这就是IntoIterator做。因此,如果您采用 IntoIterator输入,由调用者来正确设置,例如Vec 有impl IntoIterator for Vec<T>impl IntoIterator for &[T] ,这样调用者就可以传入&v - 尽管这需要一些适应,因为 IntoIterator对于 &[T]产量Iterator<Item=&T> ,所以这里是 Iterator<Item=&&str>这是行不通的。

但自从每 Iterator还实现 IntoIterator这也不是真正的问题:调用者可以做一些调整。无论如何,我的观点是该函数按原样工作,需要更改的是调用者。

现在具体的片段:

let some_str: String = get_file_contents();
let lines = some_str.lines();
println!("Filtered lines: {:?}", filter_lines(&lines));
println!("All lines: {:?}", lines.collect::<Vec<&str>>());

无法工作,因为 Lines Iterator 它将被任何迭代消耗:Rust迭代器不是“可重放的”,它只是“可迭代的”可能。但尝试和重用也没有多大意义 lines() ,那会做什么?

  • 它可以重做整个事情,但只需调用 some_str.lines()两次并并行创建两个迭代器

  • 或者它可以记住整个事情,但只是 collect() Lines预先并迭代后续的 Vec不管你需要什么,这基本上就是你所期望的,并且由于你无论如何都要收集到 vec 来打印所有行,你最好先这样做:

    let some_str: String = String::from("foo\nbar\nbaz");
    let lines = some_str.lines().collect::<Vec<_>>();
    // Vec<&str> -> Iterator<Item=&&str> -> Iterator<Item=&str>
    println!("Filtered lines: {:?}", filter_lines(lines.iter().map(|s| *s)));
    println!("All lines: {:?}", lines);
    

关于rust - 在 Rust 中定义采用迭代器而不消耗的函数的惯用方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65108801/

相关文章:

java - 在特定时间后中断迭代的最佳方法是什么?

java - 如果 Arraylist 中存在元素而不引发 ConcurrentModificationException,则从 Arraylist 中移除这些元素

c++ - 有没有办法使用独立的 `std::begin` 和 const_iterator?

rust - 具有类似图形的数据结构的多个可变借用

rust - `Iterator::inspect` 中闭包的副作用是否定义明确,因此它可以用于例如数数?

python - 为什么在使用 cdecl Rust 函数和 Python 的 Ctypes 传递指向切片的指针时会出现调用约定不匹配?

rust - 有没有办法显示 Result 作为返回类型是多余的警告?

ruby-on-rails - rails : An elegant way to display a message when there are no elements in database

Rust 泛型, "` T` 可能活得不够长”

rust - 在没有转变的情况下为拥有的奶牛重用向量堆空间?