I've been working through the 2020 Advent of Code in Rust, trying to produce solutions involving fairly generic functions, which could (in theory) be reused elsewhere without having to worry too much about types. For day 2, the problem involves checking each line of a file to make sure that it is a string conforming to some given condition. My current approach is to define a function similar to the following:
fn filter_lines<'a>(lines: impl IntoIterator<Item = &'a str>) -> Vec<&'a str> {
lines
.into_iter()
.filter(|line| verify_line(line))
.collect()
}
Here, verify_line
is just some function which takes an &str
and produces a boolean depending on whether the line conforms to a certain specification.
The issue is that this function appears to necessarily consume the iterator. I've tried rewriting it as follows to use a reference to lines
, and clone lines
before operating on this, but this code won't compile:
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()
}
The compiler produces the following error:
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
I think I understand why doing this isn't allowed, but I'm not sure how to idiomatically define a function such as filter_lines
which won't consume the iterator. So, what would be a better way to implement this filter_lines
function such that it can be called as follows?
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>>());