Understanding BufReader and iteration through lines

#1

Hi,

Again some advice needed. Let say I want to read a file line-by-line. Usually what I do is to open a file and call a reader on it (BufReader). Example:

for line in BufReader::new(file).lines() {
   let l = line.unwrap();
   println!("{}", l);
}

Now suppose i want to parse the reading by outsourcing it to a function so that the unwrapping and any other preprocessing prior to printing, gets executed before returning a line. So I want to create an API for line reading that, for example skips lines that start with #:

fn pure_lines(file: &str)-> &'a str{
    let mut line = String::new();
    match file.read_line(&mut line) {
                Ok(b) => {b},
                Err(_) => {},
     }
    let re = Regex::new(r"^#.*").unwrap();
    match re.captures(&line) {
            Some(_m) => {

                continue;
            }
            None => {
               line
            }
   Ok(())
}

----------------------
fn main() {


  let fp = File::open("somefile")?;

  for line in pure_lines(fp) {
     println!("code line: {}", line);
  }

}

The example obviously does not work and has inconsistencies (please any pointers there are more than welcomed), so read it more as a pseudo code rather than the actual code, but any help pushing me in the right direction would be more that appreciated !!

Thank you !

m

PS

I understand there are alternatives to the above solution but what I am trying to achieve is a standardized API for a library I already am playing with (C lib ).

0 Likes

#2

When function returns a temporary reference to some value (&-something), it generally can’t be anything newly created or read in the function. It’s supposed be a value that already existed before the function was called. This means that returning &str 99% of the time is not what you want. Returning this type is useful for getter methods, and pretty much nothing else.

Every variable you create in the function will be destroyed before the function finishes. So you can’t return a reference to anything held in a variable (like your line variable), because target of that reference will be destroyed before you return it.

You will have to return String, because that type is owned and can be moved out of the function, unlike a temporary borrow. If you’d like to signal difference between the continue case and a returned string, use Option<String> and return None when there’s nothing useful to return.

Alternatively, take line: &mut String as a function argument and fill it in with the line.


Going further, to make for line in pure_lines() work, you’ll have to make an iterator. There are two approaches to this:

  1. Make a custom struct, and implement Iterator (fn next()) on it, or

  2. Use an existing iterator, and modify it with .map()/.filter(), etc, and return it as -> impl Iterator<Item=String>.

1 Like

#3

thank you very much ! this was very helpful :slight_smile:

0 Likes