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
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);
f.flush()?;
VERY IMPORTANT
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)
where
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
}