Rust File IO

See std::io for more details.

For dealing with the filesystem, such as opening or deleting files, see std::fs.

For manipulating paths, see std::path.

For low-level network IO, see std::net.

One Liners for Reading the Entire File

Use read_to_string and read.

These are both fast as they allocate a buffer of the required size to start with.

let contents: String = std::fs::read_to_string("/some/file")?;
let bytes: Vec<u8> = std::fs::read("/some/file")?;

One Liners for Writing the Entire File

Use write. The file will be created if it does not exist, replaced if it does.

The contents is anything that implements AsRef<[u8]> - which includes String and str.

let contents = "Some data!";
std::fs::write("/some/file", &data);

Reading Files Line by Line

You should use BufReader to speed up reading. Note that you need to bring the BufRead trait into scope to get access to the lines method.

use std::fs::File;
use std::io::{BufRead, BufReader};

fn main() {
    let f = File::open("/some/file").unwrap();
    let f = BufReader::new(f);

    for line in f.lines() {
        let line = line.expect("Unable to read line");
        println!("{}", line);

Lines splits on LF (0xA) or CRLF (0xD,0xA). To split on other bytes, use the split method.

Getting All Lines From a File into a Vector

let lines = reader.lines().collect::<io::Result<Vec<String>>>()?;


Writing is mainly done using macros, namely write! and writeln!. See fmt for the juicy stuff.

If you’re looking for functionality equivalent to C#’s TextWriter such as Write(UInt32), this is how you do it, using the macros.

let f = File::open("/some/file").unwrap();
let f = BufWriter::new(f);
writeln!(f, "Hello {}, you are {} years old", name, age);


Rigorous code should call flush explicitly rather than just letting Rust do it when the BufWriter is dropped. This is because any errors from flush will be squashed inside drop. It is better to flush explicitly so that errors are surfaced to the program.

Looking for the BufWrite trait to mirror BufRead? There is no such trait! Because there are no extra methods (over and above those on write) which BufWrite would add.

Generic Code

It is typical to write code that is generic over Read and Write traits - or BufRead and BufWrite. Note that the references are mutable.

fn copy<R,W>(reader: &mut R, writer: &mut W)
    R: Read,
    W: Write

Stdin, Stdout and Stderr

Rust (and C also) guard reads and writes to the standard IO file descriptors using a global lock. That lock will be obtained separately for every call to println!, for example.

To speed things up, you can obtain the lock once at the start of a writing session and hold it until done.

The lock method returns a StdoutLock which implements Write, so you can use the write! and writeln macros with it.

use std::io::prelude::*;

fn main() {
    let stdout = std::io::stdout();
    let mut writer = stdout.lock();
    writeln!(writer, "Hello");
    writeln!(writer, " World");
    // lock released here

Same applies to stdin and stderr

comments powered by Disqus