File System Operations
write()
In Rust file I/O, write refers to three distinct APIs with important behavioral differences:
std::fs::write(path, contents) -> io::Result<()>: High-level convenience function that creates (or truncates)path, writes all bytes ofcontents(C: AsRef<[u8]>), and closes the file.Write::write(&mut self, buf: &[u8]) -> io::Result<usize>: Low-level trait method that writes at mostbuf.len()bytes in a single system call and returns the number of bytes actually written (may be less thanbuf.len()!).Write::write_all(&mut self, buf: &[u8]) -> io::Result<()>: Trait method that loops calling.write()until every byte ofbufhas been written.
Comparing fs::write, Write::write, Write::write_all, and write!
Which Write API Should You Call?
|
+-- Have a file path & the full `[u8]` / `&str` ready in memory?
| +---> `std::fs::write(path, data)?` (1 line: opens, writes all, closes)
|
+-- Writing a `[u8]` slice to an already-open `File` / `BufWriter` / `TcpStream`?
| +---> `stream.write_all(bytes)?` (Handles short writes & EINTR automatically!)
|
+-- Writing formatted text (`"{id}: {name}"`) to an open stream?
| +---> `write!(stream, "{id}: {name}")?` or `writeln!(stream, ...)?`
|
+-- Implementing a custom `Write` adapter or low-level nonblocking loop?
+---> `stream.write(bytes)?` (Inspect returned `usize` count of written bytes!)
| API | Accepts | Handles Short Writes? | Returns |
|---|---|---|---|
std::fs::write(path, data) |
path: impl AsRef<Path>, data: impl AsRef<[u8]> |
Yes (calls write_all internally) |
io::Result<()> |
stream.write_all(bytes) |
bytes: &[u8] |
Yes | io::Result<()> |
write!(stream, "fmt") |
Format string + args | Yes (calls write_all per segment) |
io::Result<()> |
stream.write(bytes) |
bytes: &[u8] |
No! (Caller must check returned usize!) |
io::Result<usize> |
Demonstrating fs::write, write_all, and Vectored Writes (write_vectored)
In addition to write_all, std::fs::File and sockets support vectored I/O (write_vectored(&[IoSlice::new(a), IoSlice::new(b)]) — writev on Unix, WSASend on Windows) to gather multiple discontiguous byte slices in a single kernel call:
use std::fs::{self, File};
use std::io::{self, IoSlice, Write};
fn main() -> io::Result<()> {
let path1 = std::env::temp_dir().join("rust_ch50_fswrite.txt");
let path2 = std::env::temp_dir().join("rust_ch50_vectored.txt");
// 1. One-shot `std::fs::write` (accepts `&str`, `String`, `&[u8]`, `Vec<u8>`):
fs::write(&path1, "written via std::fs::write\n")?;
// 2. Writing multiple slices to an open `File` via `write_all` and `IoSlice`:
{
let mut f = File::create(&path2)?;
f.write_all(b"prefix: ")?;
let slices = [IoSlice::new(b"part_a + "), IoSlice::new(b"part_b\n")];
let _ = f.write_vectored(&slices)?;
}
print!("path1: {}", fs::read_to_string(&path1)?);
print!("path2: {}", fs::read_to_string(&path2)?);
fs::remove_file(&path1)?;
fs::remove_file(&path2)?;
Ok(())
}
path1: written via std::fs::write
path2: prefix: part_a + part_b