Loading pageā¦
Rust walkthroughs
Loading pageā¦
tempfile::tempfile and tempfile::tempdir for temporary resource management?tempfile::tempfile creates an anonymous temporary file in the system's temporary directory that is automatically deleted when the file handle is closed, while tempfile::tempdir creates a temporary directory with a persistent path that is automatically deleted (along with its contents) when the TempDir guard is dropped. The key distinction is scope and accessibility: tempfile() returns a File handle without exposing a filesystem path, making it ideal for scratch data that never needs to be accessed by other processes, while tempdir() returns a TempDir guard that provides a PathBuf to the directory, enabling you to create multiple files within it and share paths with external tools. Both leverage RAII for automatic cleanup, but tempfile offers stronger deletion guarantees through OS-level support on most platforms, whereas tempdir cleanup depends on the destructor running successfully.
use tempfile::tempfile;
use std::io::{Write, Read, Seek, SeekFrom};
fn main() -> std::io::Result<()> {
// tempfile() creates an anonymous temporary file
// No filesystem path is exposed
let mut file = tempfile()?;
// Write to the file
writeln!(file, "Hello, temporary file!")?;
// Seek back to read
file.seek(SeekFrom::Start(0))?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
println!("Contents: {}", contents);
// File is automatically deleted when `file` goes out of scope
// On Unix, it's already unlinked from filesystem
// On Windows, it's deleted when the handle is closed
Ok(())
}tempfile() creates an unnamed file that's automatically cleaned up when closed.
use tempfile::tempdir;
use std::fs::File;
use std::io::Write;
use std::path::Path;
fn main() -> std::io::Result<()> {
// tempdir() creates a temporary directory
// The directory path is accessible
let dir = tempdir()?;
println!("Temporary directory: {:?}", dir.path());
// Create files inside the directory
let file_path = dir.path().join("data.txt");
let mut file = File::create(&file_path)?;
writeln!(file, "Hello from tempdir!")?;
// Can create multiple files
let config_path = dir.path().join("config.json");
let mut config = File::create(&config_path)?;
writeln!(config, r#"{{"setting": true}}"#)?;
// List files in the directory
for entry in std::fs::read_dir(dir.path())? {
let entry = entry?;
println!("Found: {:?}", entry.path());
}
// Directory and all contents deleted when `dir` goes out of scope
Ok(())
}tempdir() creates a named directory you can use to organize multiple temporary files.
use tempfile::{tempfile, tempdir};
use std::fs;
fn main() -> std::io::Result<()> {
// tempfile cleanup: Immediate and reliable
// On Unix: file is unlinked immediately after creation
// The file never has a directory entry
// Space is reclaimed when handle is closed
{
let _file = tempfile()?;
// File exists but has no directory entry
// Even if program crashes, OS reclaims the space
} // Handle closed, resources freed
// tempdir cleanup: Depends on destructor
// Directory and contents deleted when TempDir is dropped
let dir_path;
{
let dir = tempdir()?;
dir_path = dir.path().to_path_buf();
// Create a file inside
fs::write(dir.path().join("test.txt"), "data")?;
// Path exists
assert!(dir_path.exists());
// If we forget to drop, cleanup won't happen
// std::mem::forget(dir); // Uncommenting leaks the directory
} // dir dropped, directory deleted
// Directory no longer exists
assert!(!dir_path.exists());
Ok(())
}tempfile has stronger cleanup guarantees because the OS handles it; tempdir relies on Rust's destructor.
use tempfile::{tempfile, NamedTempFile, tempdir};
use std::path::PathBuf;
fn main() -> std::io::Result<()> {
// tempfile() - anonymous, no path
let anon_file = tempfile()?;
// Cannot get a path - the file doesn't have one
// (or it's already unlinked on Unix)
// Useful for:
// - Scratch space for computation
// - Memory-mapped temporary storage
// - Intermediate data processing
// tempdir() - named directory, files have paths
let dir = tempdir()?;
let file_path = dir.path().join("output.txt");
// Path can be shared with:
// - Child processes
// - External commands
// - C libraries that need file paths
// NamedTempFile - middle ground
// Creates a temp file WITH a path
use tempfile::NamedTempFile;
let named_temp = NamedTempFile::new()?;
println!("Named temp file path: {:?}", named_temp.path());
// Path is available but file is still temporary
// Persist the file if needed
let persisted_path = named_temp.into_temp_path();
// Can also: named_temp.persist("/permanent/path.txt")?
Ok(())
}tempfile() is anonymous; tempdir() provides paths; NamedTempFile offers a middle ground.
use tempfile::tempdir;
use std::process::Command;
fn main() -> std::io::Result<()> {
// tempdir is ideal when external processes need file paths
let dir = tempdir()?;
// Create a script file
let script_path = dir.path().join("script.sh");
std::fs::write(&script_path, "#!/bin/sh\necho 'Hello from script'")?;
// Run external command with temp files
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
std::fs::set_permissions(&script_path,
std::fs::Permissions::from_mode(0o755))?;
let output = Command::new(&script_path)
.current_dir(dir.path())
.output()?;
println!("Script output: {}", String::from_utf8_lossy(&output.stdout));
}
// tempfile() wouldn't work here - no path to pass
Ok(())
}tempdir() enables scenarios where external tools need filesystem paths.
use tempfile::tempdir;
use std::fs::File;
use std::io::Write;
fn main() -> std::io::Result<()> {
// tempdir excels at organizing multiple related temp files
let dir = tempdir()?;
// Create organized structure
let data_dir = dir.path().join("data");
let cache_dir = dir.path().join("cache");
std::fs::create_dir(&data_dir)?;
std::fs::create_dir(&cache_dir)?;
// Create multiple data files
for i in 0..3 {
let path = data_dir.join(format!("file_{}.txt", i));
let mut file = File::create(path)?;
writeln!(file, "Data file {}", i)?;
}
// Create cache files
let mut cache_file = File::create(cache_dir.join("cache.bin"))?;
cache_file.write_all(b"cached data")?;
// Everything is cleaned up together
println!("Created temporary structure at: {:?}", dir.path());
// Recursive cleanup handles all nested files and directories
Ok(())
}tempdir() provides a scope for multiple temporary resources that are cleaned up together.
use tempfile::tempdir;
use std::path::PathBuf;
fn main() -> std::io::Result<()> {
// tempdir allows keeping the directory if needed
let dir = tempdir()?;
// Do some work...
std::fs::write(dir.path().join("output.txt"), "results")?;
// Option 1: Let it be cleaned up (default)
// dir will be deleted when it goes out of scope
// Option 2: Keep the directory
let kept_path: PathBuf = dir.into_path();
// Now the directory won't be deleted automatically
println!("Directory kept at: {:?}", kept_path);
// You're now responsible for cleanup
// std::fs::remove_dir_all(&kept_path)?;
// For tempfile, there's no way to "keep" it
// (Use NamedTempFile if you need this for files)
Ok(())
}tempdir() can be converted to a regular path, transferring cleanup responsibility.
use tempfile::tempdir;
use std::fs;
use std::io;
fn process_files() -> io::Result<()> {
let dir = tempdir()?;
// Create intermediate files
let input = dir.path().join("input.txt");
fs::write(&input, "input data")?;
// Process might fail
let content = fs::read_to_string(&input)?;
if content.is_empty() {
// Early return - dir is still cleaned up
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Empty input"
));
}
// More processing...
let output = dir.path().join("output.txt");
fs::write(&output, "processed data")?;
// Success - dir cleaned up on return
Ok(())
}
fn main() {
match process_files() {
Ok(()) => println!("Success"),
Err(e) => println!("Error: {}", e),
}
// In both success and error cases,
// the temporary directory is cleaned up
}RAII ensures cleanup happens even when errors cause early returns.
use tempfile::{tempfile_in, tempdir_in, tempfile, tempdir};
use std::path::Path;
fn main() -> std::io::Result<()> {
// By default, uses system temp directory
// Can specify custom location
let custom_dir = tempdir_in("/tmp/myapp")?;
println!("Custom temp dir: {:?}", custom_dir.path());
let custom_file = tempfile_in("/tmp/myapp")?;
// File created in specified directory
// For more control, use Builder
use tempfile::Builder;
let named_temp = Builder::new()
.prefix("myapp_")
.suffix("_temp")
.rand_bytes(5)
.tempfile()?;
let dir = Builder::new()
.prefix("session_")
.tempdir()?;
println!("Session dir: {:?}", dir.path());
Ok(())
}Both tempfile and tempdir support custom locations and name prefixes.
use tempfile::tempfile;
fn main() -> std::io::Result<()> {
// tempfile() behavior varies by platform:
// Unix:
// - Creates file with O_TMPFILE (Linux 3.11+) or
// - Creates file then immediately unlinks it
// - File never has visible directory entry
// - Deleted automatically when closed
// - Works even if program crashes
// Windows:
// - Creates file with FILE_FLAG_DELETE_ON_CLOSE
// - Deleted when last handle is closed
// - If program crashes, OS still cleans up
// tempdir() behavior:
// All platforms:
// - Creates directory with unique name
// - Deletes directory and contents in destructor
// - If destructor doesn't run (panic, abort, etc.),
// directory may persist
let _file = tempfile()?;
// On any platform, when _file is dropped,
// the file is cleaned up by the OS
Ok(())
}tempfile has OS-level cleanup guarantees; tempdir relies on destructors.
use tempfile::tempfile;
use std::io::{Write, Seek, SeekFrom};
#[cfg(unix)]
fn main() -> std::io::Result<()> {
// tempfile is ideal for memory-mapped scratch space
use memmap2::MmapMut;
let mut file = tempfile()?;
// Set file size
file.seek(SeekFrom::Start(1024 * 1024))?; // 1 MB
file.write_all(&[0])?;
file.seek(SeekFrom::Start(0))?;
// Memory map the file
let mut mmap = unsafe { MmapMut::map_mut(&file)? };
// Use as random-access memory
mmap[0] = 42;
mmap[1000] = 255;
println!("First byte: {}", mmap[0]);
// Changes are written to file
// File is cleaned up when mmap and file are dropped
Ok(())
}
#[cfg(not(unix))]
fn main() -> std::io::Result<()> {
println!("Memory mapping example requires Unix");
Ok(())
}tempfile() works well for memory-mapped temporary storage that's automatically cleaned up.
fn main() {
// | Aspect | tempfile() | tempdir() |
// |---------------------|-------------------------|-------------------------|
// | Returns | File handle | TempDir guard |
// | Has path | No | Yes |
// | Create files inside | No | Yes |
// | Share with extern | No | Yes |
// | Cleanup guarantee | OS-level | Destructor-based |
// | Multiple resources | No | Yes |
// | Persist option | No (use NamedTempFile) | Yes (into_path) |
// | Use case | Scratch data | Organized temp files |
println!("Comparison documented above");
}When to use tempfile():
| Use Case | Reason | |----------|--------| | Scratch space for computation | No path needed, auto-cleanup | | Memory-mapped temporary files | OS-level cleanup is reliable | | Intermediate data processing | Isolated, anonymous storage | | Cache that doesn't need persistence | Strong cleanup guarantee |
When to use tempdir():
| Use Case | Reason | |----------|--------| | Multiple related temp files | Organize under one directory | | External process integration | Can pass paths to commands | | Hierarchical temp structure | Create subdirectories | | Files needed by other code | Path can be shared |
Key differences:
| Property | tempfile() | tempdir() | |----------|------------|-----------| | Anonymous | Yes | No | | Path available | No | Yes | | Cleanup mechanism | OS-level | Destructor | | Crash-safe cleanup | Yes | Not guaranteed | | Multiple resources | Single file | Directory tree | | Persistence option | NamedTempFile | into_path() |
Key insight: The choice between tempfile() and tempdir() is fundamentally about visibility: tempfile() creates an anonymous resource that's perfect for internal use where no path is needed, while tempdir() creates a named directory that's essential when you need to share file paths with other parts of your code or external processes. The cleanup difference matters too: tempfile() benefits from OS-level deletion guarantees that work even if your program crashes or is killed, while tempdir() cleanup depends on Rust's destructor running. For scratch data that never leaves your process, tempfile() is cleaner and safer; for anything that needs a filesystem presence, tempdir() provides the necessary structure. Both embody Rust's RAII pattern, but with different levels of guaranteeātempfile()'s guarantee is stronger because the OS handles it independently of your program's lifecycle.