Read & Write

Read lines of strings from a file

std cat-filesystem

Writes a three-line message to a file, then reads it back a line at a time with the std::io::Lines⮳ iterator created by std::io::BufRead::linesstd::fs::File⮳ implements std::io::Read⮳ which provides std::io::BufReader⮳ trait. std::fs::File::create⮳ opens a std::fs::File⮳ for writing, std::fs::File::open⮳ for reading.

use std::fs;
use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
use std::io::Error;
use std::io::Write;

fn main() -> Result<(), Error> {
    if !fs::exists("temp")? {
        fs::create_dir("temp")?;
    }
    let path = "temp/lines.txt";

    let mut output = File::create(path)?;
    write!(output, "Rust\n💖\nFun")?;

    let input = File::open(path)?;
    let buffered = BufReader::new(input);

    for line in buffered.lines() {
        println!("{}", line?);
    }

    Ok(())
}

Avoid writing and reading from the same file

same-file cat-filesystem

Use same_file::Handle⮳ to a file that can be tested for equality with other handles. In this example, the handles of file to be read from and to be written to are tested for equality.

use std::fs;
use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
use std::io::Error;
use std::io::ErrorKind;
use std::path::Path;

use same_file::Handle;

fn main() -> Result<(), Error> {
    if !fs::exists("temp")? {
        fs::create_dir("temp")?;
    }
    std::fs::write("temp/new.txt", b"Lorem ipsum")?;

    let path_to_read = Path::new("temp/new.txt");
    let stdout_handle = Handle::stdout()?;
    let handle = Handle::from_path(path_to_read)?;

    if stdout_handle == handle {
        return Err(Error::new(
            ErrorKind::Other,
            "You are reading and writing to the same file",
        ));
    } else {
        let file = File::open(path_to_read)?;
        let file = BufReader::new(file);
        for (num, line) in file.lines().enumerate() {
            println!("{} : {}", num, line?.to_uppercase());
        }
    }

    Ok(())
}
cargo run

displays the contents of the file new.txt.

cargo run >> new.txt

errors because the two files are same.

Access a file randomly using a memory map

memmap2 cat-filesystem

Creates a memory map of a file using memmap2⮳ and simulates some non-sequential reads from the file. Using a memory map means you just index into a slice rather than dealing with std::fs::File::seek⮳ to navigate a std::fs::File⮳.

The memmap2::Mmap::map⮳ function assumes the file behind the memory map is not being modified at the same time by another process or else a race condition⮳ occurs.

use std::fs;
use std::fs::File;
use std::io::Error;
use std::io::Write;

use memmap2::Mmap;

fn main() -> Result<(), Error> {
    if !fs::exists("temp")? {
        fs::create_dir("temp")?;
    }
    write!(
        File::create("temp/content.txt")?,
        "My hovercraft is full of eels!"
    )?;
    let file = File::open("temp/content.txt")?;
    let map = unsafe { Mmap::map(&file)? };
    println!("{:?}", map);
    let random_indexes = [0, 1, 2, 19, 22, 10, 11, 29];
    assert_eq!(&map[3..13], b"hovercraft");
    let random_bytes: Vec<u8> =
        random_indexes.iter().map(|&idx| map[idx]).collect();
    assert_eq!(&random_bytes[..], b"My loaf!");
    Ok(())
}