File System Operations
read_to_string()
Rust provides read_to_string in three places in the standard library—all of which read an input source until End-of-File (EOF) and validate that the bytes are valid UTF-8:
std::fs::read_to_string(path) -> io::Result<String>: Opens a file atpath, pre-allocates aStringmatching the file's metadata byte length, reads the entire file, and closes it.std::io::read_to_string(reader) -> io::Result<String>: Takes anyR: Read(such asio::stdin()or aTcpStream/Take<File>) and returns a newly allocatedString.Read::read_to_string(&mut self, buf: &mut String) -> io::Result<usize>: Method on anyReadimplementor that appends the read UTF-8 text onto an existingStringbuffer (reusing its capacity) and returns the number of bytes appended.
Comparing the Three read_to_string APIs
| Function / Method | Input Argument | Allocation Behavior | Error Behavior on Non-UTF-8 Bytes |
|---|---|---|---|
fs::read_to_string(path) |
P: AsRef<Path> |
Queries file.metadata().len() to pre-allocate exact String capacity in 1 allocation |
Returns Err(ErrorKind::InvalidData) |
io::read_to_string(reader) |
R: Read |
Allocates and grows a new String |
Returns Err(ErrorKind::InvalidData) |
reader.read_to_string(&mut buf) |
&mut self, buf: &mut String |
Appends into caller's buf (can reuse buf across files after buf.clear()!) |
Returns Err(ErrorKind::InvalidData) and leaves buf unmodified! |
Notice the strong exception-safety guarantee of reader.read_to_string(&mut buf): if the stream contains invalid UTF-8 bytes or encounters an I/O error mid-read, any partial bytes read during that call are rolled back so buf retains the exact valid UTF-8 string it had before the call!
use std::fs;
use std::io::{self, Read};
fn main() -> io::Result<()> {
let path_a = std::env::temp_dir().join("rust_ch50_rts_a.txt");
let path_b = std::env::temp_dir().join("rust_ch50_rts_b.txt");
fs::write(&path_a, "First file content.\n")?;
fs::write(&path_b, "Second file content.\n")?;
// 1. One-shot `std::fs::read_to_string(path)`:
let one_shot: String = fs::read_to_string(&path_a)?;
print!("one_shot: {}", one_shot);
// 2. Reusing a single `String` buffer across multiple files with `Read::read_to_string`:
let mut reusable_buf = String::with_capacity(256);
for path in [&path_a, &path_b] {
reusable_buf.clear();
fs::File::open(path)?.read_to_string(&mut reusable_buf)?;
println!(
"read {} bytes into reusable_buf (capacity stays {} B): {:?}",
reusable_buf.len(),
reusable_buf.capacity(),
reusable_buf.trim()
);
}
fs::remove_file(&path_a)?;
fs::remove_file(&path_b)?;
Ok(())
}
one_shot: First file content.
read 20 bytes into reusable_buf (capacity stays 256 B): "First file content."
read 21 bytes into reusable_buf (capacity stays 256 B): "Second file content."