我一直在努力度过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/