I want to read a file line by line:
- Skip lines that start with
'#'
- Print (or otherwise use them a.k.a. borrow) if they don't start with '#'
I tried two different ways and I don't think I understand Rust enough to write it properly.
Approach 1
I got the file to the BufReader
, and also get BufReader
via reference using by_ref
.
My problem is that I don't know how to say "I don't want to modify the line just read the first byte". I also tried a byte representation of the string, but it looks ugly for the code example
use std::{
fs::File,
io::{self, BufRead, BufReader, Read},
};
fn main() -> io::Result<()> {
let fname = "/etc/hosts";
let file = File::open(fname)?;
let mut br = BufReader::new(file);
for line in br.by_ref().lines() {
if line.unwrap().starts_with("#") {
continue;
}
println!("{}", line?);
}
Ok(())
}
The compiler response is every time: "I cannot give you the line as it is already moved for check...".
error[E0382]: use of moved value: `line`
--> src/main.rs:15:24
|
11 | for line in br.by_ref().lines() {
| ---- move occurs because `line` has type `std::result::Result<std::string::String, std::io::Error>`, which does not implement the `Copy` trait
12 | if line.unwrap().starts_with("#") {
| ---- value moved here
...
15 | println!("{}", line?);
| ^^^^ value used here after move
Approach 2
I thought I can use a filter. I didn't want to go this way, as most the people with more sequential programming habits do not like to use functional patterns for such operations like file operations. We prefer to control buffer sizes, and when what happen, and functional approaches usually make it much more difficult.
I thought I should post that I know that this can be done in a functional way, but after I wrote and run it runs out that is the same problem...
use std::{
fs::File,
io::{self, BufRead, BufReader},
};
fn main() -> io::Result<()> {
let fname = "/etc/hosts";
let file = File::open(fname)?;
let lines: io::Result<std::string::String> = BufReader::new(file)
.lines()
.filter(|x| x.unwrap().starts_with('#'))
.collect();
for line in lines {
println!("{}", line);
}
Ok(())
}
I got an error:
error[E0507]: cannot move out of borrowed content
--> src/main.rs:12:21
|
12 | .filter(|x| x.unwrap().starts_with('#'))
| ^ cannot move out of borrowed content
Changing the lambda argument x
to a reference will cause borrowing which will cause another response from the compiler: cannot move out of borrowed content
.
Is there any easy way to write such patterns without copying value every time?