Search code examples
iorustbufferedreader

How can I stop a BufReader from reading in Rust when using read_until()?


I am attempting to use a BufReader to load a bunch of data and then use read_until() scan the data. However, I am having a hard time discerning when read_until() hits EOF and my code wraps back around to the start of the data again, creating an infinite loop. I need to stop reading when read_until() hits EOF. How can I accomplish this in Rust?

This is what I have so far:

use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
use std::str;

fn main() -> std::io::Result<()> {
    let f1 = File::open("foo.txt")?;
    let mut reader = BufReader::new(f1);
    let mut byte_vec: Vec<u8> = Vec::new();
    loop {
        let my_bytes = reader.read_until(b'\n', &mut byte_vec);
        let is_valid_utf8 = str::from_utf8(&byte_vec);

        match is_valid_utf8 {
            Ok(the_str) => println!("{} is a valid UTF-8 String", the_str),
            Err(err) => println!("Error: {}", err),
        }
    }
    Ok(())
}

foo.txt just has a few lines of example text. The code will loop forever back to the beginning of the file.


Solution

  • Check the warnings that the compiler is giving you, that's why they are there!

    warning: unreachable expression
      --> src/lib.rs:16:5
       |
    16 |     Ok(())
       |     ^^^^^^
       |
       = note: #[warn(unreachable_code)] on by default
    
    warning: unused variable: `my_bytes`
     --> src/lib.rs:8:13
      |
    8 |         let my_bytes = reader.read_until(b'\n', &mut byte_vec);
      |             ^^^^^^^^ help: consider using `_my_bytes` instead
      |
      = note: #[warn(unused_variables)] on by default
    

    The compiler is telling you that

    1. Your loop will never exit — that's your infinite loop.
    2. You aren't using the return value of read_until.

    These two things are related. Check the docs for read_until, emphasis mine:

    Read all bytes into buf until the delimiter byte or EOF is reached.

    [...]

    If successful, this function will return the total number of bytes read.

    Use the value:

    let my_bytes = reader.read_until(b'\n', &mut byte_vec)?;
    if my_bytes == 0 { break };
    

    Continue reading the docs, emphasis mine:

    all bytes up to, and including, the delimiter (if found) will be appended to buf

    Your byte_vec will continue to accumulate every previous line. This is why you believe that the BufReader is returning to the beginning of the input. You probably wish to clear it at the end of each loop iteration.

    See also: