File Handles and Resource Management
In Rust, std::fs::File owns an operating-system file descriptor (fd on Unix, HANDLE on Windows) and closes it automatically when the File value is dropped. There is no File::close() method in the standard library because closing a file while keeping the File variable in scope would leave a dangling descriptor handle.
Automatic Closing on Scope Exit and Early drop(file)
When a File goes out of scope (or is passed to drop(file)), its Drop implementation invokes the OS close system call. To close a file before opening the same path again or before executing a subsequent step in the same function, either wrap the File in an inner block { ... } or call drop(file).
use std::fs::{self, File};
use std::io::{Read, Write};
fn main() -> std::io::Result<()> {
let path = std::env::temp_dir().join("rust_ch35_file_demo.txt");
{
let mut writer = File::create(&path)?;
writer.write_all(b"deterministic file cleanup\n")?;
writer.sync_all()?;
} // `writer` is dropped and closed here.
let mut reader = File::open(&path)?;
let mut contents = String::new();
reader.read_to_string(&mut contents)?;
drop(reader); // Explicitly close `reader` before removing the file.
fs::remove_file(&path)?;
print!("read from file: {}", contents);
Ok(())
}
read from file: deterministic file cleanup
Buffering and Flushing Behavior on Drop: BufWriter<W>
Unlike File, which issues direct unbuffered OS writes on every .write() call, std::io::BufWriter<W> accumulates bytes in an in-memory buffer and flushes them to the underlying writer W when the buffer fills up or when BufWriter is dropped.
However, because Drop::drop cannot return a Result, any I/O error that occurs during the automatic flush inside BufWriter::drop is silently ignored. Furthermore, if the inner writer panics or if mem::forget is called on the BufWriter, buffered bytes are never written. Always call .flush() explicitly (or .into_inner()) before letting a BufWriter drop:
BufWriter<File> Lifecycle:
buf_writer.write_all(data) ---> Stored in RAM buffer (Vec<u8>)
|
+------------------------------+------------------------------+
| (Recommended) | (Relying only on Drop)
v v
buf_writer.flush()? BufWriter::drop(&mut self)
- Propagates I/O errors via `Result` - Flushes buffer, but silently
- Followed by File::drop() closing OS fd ignores any disk/write errors!
use std::fs::{self, File};
use std::io::{BufWriter, Write};
fn write_log_safely(path: &std::path::Path) -> std::io::Result<()> {
let file = File::create(path)?;
let mut buffered = BufWriter::new(file);
writeln!(buffered, "event=startup status=ok")?;
writeln!(buffered, "event=listen port=8080")?;
// Explicitly flush and surface any I/O error before `BufWriter` drops:
buffered.flush()?;
Ok(())
}
fn main() -> std::io::Result<()> {
let path = std::env::temp_dir().join("rust_ch35_bufwriter_demo.log");
write_log_safely(&path)?;
let text = fs::read_to_string(&path)?;
fs::remove_file(&path)?;
print!("{}", text);
Ok(())
}
event=startup status=ok
event=listen port=8080
Self-Deleting Temporary File Guard Pattern
Wrapping a PathBuf in a struct whose Drop implementation calls let _ = std::fs::remove_file(&self.path); ensures temporary scratch files are removed from disk even if a test or build step fails mid-execution.