1

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?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Mazeryt
  • 855
  • 1
  • 18
  • 37
  • I saw that post, but I cannot figure out base on how to fix my code. I was trying to use some match expression but is still the same issue :( Should I introduce Box here to wrap the line? – Mazeryt May 24 '19 at 15:48
  • `Option::unwrap` consumes the `Option`. Use `Option::as_ref`: `line.as_ref().unwrap().starts_with("#")`. [The duplicate applied to your question](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=2bd7fa8bd8a55a9efb076b7cdad86b17). – Shepmaster May 24 '19 at 15:50
  • You right, I used stupidly it on the BufReader instead of the line. Need to get a better understanding of this interface. – Mazeryt May 24 '19 at 15:57

0 Answers0