Reading Files in Rust

Our first program will demonstrate reading files in Rust. Here’s the full source code with explanations.

use std::fs::File;
use std::io::{self, Read, Seek, SeekFrom};
use std::path::Path;

// This helper function will streamline our error checks
fn check(result: io::Result<()>) {
    if let Err(e) = result {
        panic!("{}", e);
    }
}

fn main() -> io::Result<()> {
    // Perhaps the most basic file reading task is
    // slurping a file's entire contents into memory.
    let contents = std::fs::read_to_string("/tmp/dat")?;
    print!("{}", contents);

    // You'll often want more control over how and what
    // parts of a file are read. For these tasks, start
    // by opening a file to obtain a File value.
    let mut f = File::open("/tmp/dat")?;

    // Read some bytes from the beginning of the file.
    // Allow up to 5 to be read but also note how many
    // actually were read.
    let mut buffer = [0; 5];
    let n = f.read(&mut buffer)?;
    println!("{} bytes: {}", n, String::from_utf8_lossy(&buffer[..n]));

    // You can also seek to a known location in the file
    // and read from there.
    f.seek(SeekFrom::Start(6))?;
    let mut buffer = [0; 2];
    let n = f.read(&mut buffer)?;
    println!("{} bytes @ {}: {}", n, 6, String::from_utf8_lossy(&buffer[..n]));

    // Seek relative to the current position,
    f.seek(SeekFrom::Current(4))?;

    // and relative to the end of the file.
    f.seek(SeekFrom::End(-10))?;

    // The io module provides some functions that may
    // be helpful for file reading. For example, reads
    // like the ones above can be more robustly
    // implemented with read_exact.
    f.seek(SeekFrom::Start(6))?;
    let mut buffer = [0; 2];
    f.read_exact(&mut buffer)?;
    println!("2 bytes @ {}: {}", 6, String::from_utf8_lossy(&buffer));

    // There is no built-in rewind, but
    // Seek::Start(0) accomplishes this.
    f.seek(SeekFrom::Start(0))?;

    // The BufReader struct implements a buffered
    // reader that may be useful both for its efficiency
    // with many small reads and because of the additional
    // reading methods it provides.
    let mut reader = io::BufReader::new(f);
    let mut buffer = Vec::new();
    reader.read_until(b'\n', &mut buffer)?;
    println!("{} bytes: {}", buffer.len(), String::from_utf8_lossy(&buffer));

    // The file is automatically closed when it goes out of scope.

    Ok(())
}

To run the program, first create a file with some content:

$ echo "hello" > /tmp/dat
$ echo "rust" >> /tmp/dat
$ cargo run
hello
rust
5 bytes: hello
2 bytes @ 6: ru
2 bytes @ 6: ru
6 bytes: hello

This example demonstrates various ways to read files in Rust, including reading the entire file at once, reading specific bytes, seeking to different positions in the file, and using buffered readers. The Rust standard library provides powerful and flexible tools for file I/O operations.

Next, we’ll look at writing files.