File System Operations
Removing Files and Directories
std::fs provides three distinct removal functions so a program can never accidentally delete a directory tree when it only intended to remove a single file or an empty directory: fs::remove_file, fs::remove_dir, and fs::remove_dir_all.
Comparison of Filesystem Removal Functions
| Function | Target Type | If Target Is a Non-Empty Directory | Symbolic Link Behavior |
|---|---|---|---|
fs::remove_file(path) |
Regular file or symlink | Fails (IsADirectory / PermissionDenied) |
Removes the symlink itself (never deletes the target of the symlink!) |
fs::remove_dir(path) |
Empty directory | Fails with ErrorKind::DirectoryNotEmpty! |
Fails on file symlinks (or removes directory junction on Windows) |
fs::remove_dir_all(path) |
Directory and all its contents recursively (rm -rf) |
Deletes all files, symlinks, and subdirectories inside path, then removes path |
Removes child symlinks without following them into external target directories |
Safety Boundary Between Removal Functions:
`fs::remove_file(p)` ---> Only unlinks a single file or symlink
`fs::remove_dir(p)` ---> Only removes a directory if it contains 0 entries (`rmdir`)
`fs::remove_dir_all(p)` ---> Recursively deletes everything inside `p` (`rm -rf`)
use std::fs;
use std::io::{self, ErrorKind};
fn main() -> io::Result<()> {
let root = std::env::temp_dir().join("rust_ch50_rm_demo");
let _ = fs::remove_dir_all(&root);
fs::create_dir_all(&root)?;
let file_path = root.join("item.txt");
fs::write(&file_path, "temporary item")?;
// 1. `remove_dir` refuses to delete a non-empty directory (`DirectoryNotEmpty`):
match fs::remove_dir(&root) {
Ok(()) => unreachable!(),
Err(e) if e.kind() == ErrorKind::DirectoryNotEmpty => {
println!("remove_dir refused non-empty directory (DirectoryNotEmpty)");
}
Err(e) => return Err(e),
}
// 2. Remove the file with `remove_file`:
fs::remove_file(&file_path)?;
println!("item.txt exists after remove_file: {}", fs::exists(&file_path)?);
// 3. Now that `root` is empty, `remove_dir` succeeds:
fs::remove_dir(&root)?;
println!("root exists after remove_dir: {}", fs::exists(&root)?);
Ok(())
}
remove_dir refused non-empty directory (DirectoryNotEmpty)
item.txt exists after remove_file: false
root exists after remove_dir: false
Idempotent Cleanup Helper (Ignoring ErrorKind::NotFound)
When cleaning up scratch files or cache directories that may or may not exist, match on ErrorKind::NotFound and map it to Ok(()) so genuine permission or I/O errors are still surfaced:
use std::fs;
use std::io::{self, ErrorKind};
use std::path::Path;
pub fn remove_file_if_exists(path: &Path) -> io::Result<()> {
match fs::remove_file(path) {
Ok(()) => Ok(()),
Err(e) if e.kind() == ErrorKind::NotFound => Ok(()),
Err(e) => Err(e),
}
}
fn main() -> io::Result<()> {
let missing = std::env::temp_dir().join("rust_ch50_already_gone.tmp");
remove_file_if_exists(&missing)?;
println!("idempotent remove_file_if_exists succeeded");
Ok(())
}
idempotent remove_file_if_exists succeeded