Files in std::fs
std::fs provides filesystem operations, while std::fs::File is an owned file handle that implements Read, Write, and Seek.
What it is
The std::fs module contains portable filesystem operations.
File represents an open file handle.
Opening a file acquires an operating-system resource.
Dropping File closes that resource.
This is a normal Rust ownership pattern, not a special cleanup mechanism.
File::open opens for reading.
File::create opens for writing, creating or truncating the file.
OpenOptions configures read, write, append, create, truncate, and related behavior.
Convenience functions such as fs::read, fs::read_to_string, and fs::write handle whole files.
Use Path and PathBuf for path values rather than stringly-typed path manipulation.
How it works
File implements The Read and Write Traits depending on how it was opened.
A file opened read-only will fail when written.
A file opened write-only will fail when read.
Most filesystem operations return io::Result<T>.
That result contains io::Error when the OS rejects the operation.
Failure can come from missing paths, permissions, invalid names, full disks, or races.
File also implements Seek, so a file cursor can be moved.
For small files, whole-file helpers are clear and fine.
For large files, stream through BufReader or BufWriter.
For atomic application-level updates, write to a temporary file and rename.
The standard library does not make multi-step filesystem workflows race-free by default.
Example
use std::fs::File;
use std::io::{self, Read, Write};
fn main() -> io::Result<()> {
let path = "rustacean-example.txt";
{
let mut file = File::create(path)?;
file.write_all(b"Ferris\n")?;
}
let mut file = File::open(path)?;
let mut text = String::new();
file.read_to_string(&mut text)?;
assert_eq!(text, "Ferris\n");
std::fs::remove_file(path)?;
Ok(())
}Example: OpenOptions
use std::fs::OpenOptions;
use std::io::{self, Write};
fn main() -> io::Result<()> {
let path = "rustacean-log.txt";
let mut log = OpenOptions::new()
.create(true)
.append(true)
.open(path)?;
writeln!(log, "started")?;
std::fs::remove_file(path)?;
Ok(())
}Best practice
- ✅ Use
fs::read_to_stringfor simple small UTF-8 files. - ✅ Use
BufReader<File>for line-oriented or streaming reads. - ✅ Use
BufWriter<File>for many small writes. - ✅ Use
OpenOptionswhen the access mode matters. - ✅ Return or propagate
io::Result<T>instead of panicking. - ✅ Treat filesystem state as changeable between checks and use operations that report failure.
- ✅ Use
PathandPathBuffor APIs that accept paths.
Pitfalls
- ⚠️
File::createtruncates an existing file. - ⚠️
fs::read_to_stringfails on invalid UTF-8. - ⚠️
fs::readandread_to_endcan allocate huge memory for huge files. - ⚠️ Checking existence before opening can race; handle the open error instead.
- ⚠️ Assuming paths are UTF-8 breaks on some platforms.
- ⚠️ Drop closes files, but explicit flushing may still matter for buffered writers.
- ⚠️ Ignoring write results can silently lose data; see Swallowing Errors.
See also
std IO & Formatting · Path and PathBuf · The Read and Write Traits · O with BufReader and BufWriter · IO Errors and io::Result · Ownership · The Drop Trait · RAII and Drop Guards · Stringly-Typed Code · Swallowing Errors
Sources
- Rust Standard Library,
std::fs— std, https://doc.rust-lang.org/std/fs/index.html - Rust Standard Library,
File— std, https://doc.rust-lang.org/std/fs/struct.File.html - Rust Standard Library,
OpenOptions— std, https://doc.rust-lang.org/std/fs/struct.OpenOptions.html
