How do I work with Tempfile for Temporary File Handling in Rust?
Walkthrough
Tempfile is a crate for creating and managing temporary files and directories in Rust. It provides secure, automatic cleanup of temporary resources, handling edge cases like race conditions and ensuring files are properly deleted when no longer needed.
Key concepts:
- NamedTempFile — A temporary file with a visible path in the filesystem
- TempDir — A temporary directory that deletes itself on drop
- SpooledTempFile — In-memory buffer that spills to disk when large
- tempfile() — Anonymous temporary file (no path, most secure)
- tempdir() — Create temporary directory with automatic cleanup
When to use Tempfile:
- Unit tests needing file I/O
- Intermediate processing files
- Download/upload buffering
- Temporary configuration files
- Cache files with automatic cleanup
When NOT to use Tempfile:
- Persistent user data
- Files that need to survive restarts
- When you need explicit user control over file location
Code Examples
Basic Temporary File
use tempfile::NamedTempFile;
use std::io::{Write, Read};
fn main() -> std::io::Result<()> {
// Create a named temporary file
let mut temp_file = NamedTempFile::new()?;
// Get the path
println!("Temp file: {:?}", temp_file.path());
// Write to it
writeln!(temp_file, "Hello, temp file!")?;
// Read it back
temp_file.seek(std::io::SeekFrom::Start(0))?;
let mut contents = String::new();
temp_file.read_to_string(&mut contents)?;
println!("Contents: {}", contents);
// File is automatically deleted when temp_file goes out of scope
Ok(())
}Temporary Directory
use tempfile::TempDir;
use std::fs::File;
use std::io::Write;
fn main() -> std::io::Result<()> {
// Create a temporary directory
let temp_dir = TempDir::new()?;
println!("Temp dir: {:?}", temp_dir.path());
// Create files inside
let file_path = temp_dir.path().join("data.txt");
let mut file = File::create(&file_path)?;
writeln!(file, "Data in temp dir")?;
// Directory and contents deleted on drop
Ok(())
}Anonymous Temporary File
use tempfile::tempfile;
use std::io::{Write, Read, Seek, SeekFrom};
fn main() -> std::io::Result<()> {
// Create an anonymous temp file (no path, more secure)
let mut file = tempfile()?;
// Write data
file.write_all(b"Anonymous temp data")?;
// Read back
file.seek(SeekFrom::Start(0))?;
let mut buf = Vec::new();
file.read_to_end(&mut buf)?;
println!("Read: {}", String::from_utf8_lossy(&buf));
// File deleted on close (or drop)
Ok(())
}Spooled Temporary File
use tempfile::SpooledTempFile;
use std::io::{Write, Read, Seek, SeekFrom};
fn main() -> std::io::Result<()> {
// In-memory buffer up to 1KB, then spills to disk
let mut spool = SpooledTempFile::new(1024);
// Small data stays in memory
write!(spool, "Small data")?;
// Check if still in memory
if spool.is_rolled_over() {
println!("Data spilled to disk");
} else {
println!("Data still in memory");
}
// Large data spills to disk
let large_data = vec![0u8; 2048];
spool.write_all(&large_data)?;
if spool.is_rolled_over() {
println!("Now spilled to disk");
}
Ok(())
}Persist Temporary File
use tempfile::NamedTempFile;
use std::io::Write;
fn main() -> std::io::Result<()> {
let mut temp = NamedTempFile::new()?;
writeln!(temp, "Important data")?;
// Persist the file at a specific location
let persisted = temp.persist("/tmp/important.txt")?;
println!("File persisted to: {:?}", persisted.path());
// File is no longer temporary - won't be deleted on drop
Ok(())
}Custom Location
use tempfile::NamedTempFile;
use std::io::Write;
fn main() -> std::io::Result<()> {
// Create temp file in specific directory
let mut temp = NamedTempFile::new_in("./output")?;
writeln!(temp, "In custom location")?;
println!("Created in: {:?}", temp.path().parent().unwrap());
Ok(())
}With Prefix and Suffix
use tempfile::Builder;
fn main() -> std::io::Result<()> {
// Named temp file with prefix/suffix
let temp = Builder::new()
.prefix("myapp_")
.suffix(".tmp")
.tempfile()?;
println!("Temp file: {:?}", temp.path());
// Temp dir with prefix
let dir = Builder::new()
.prefix("myapp_session_")
.tempdir()?;
println!("Temp dir: {:?}", dir.path());
Ok(())
}Testing with Temp Files
use tempfile::NamedTempFile;
use std::io::{Write, Read};
fn process_file(path: &std::path::Path) -> std::io::Result<String> {
let mut file = std::fs::File::open(path)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
Ok(contents.to_uppercase())
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::NamedTempFile;
use std::io::Write;
#[test]
fn test_process_file() {
let mut temp = NamedTempFile::new().unwrap();
write!(temp, "hello world").unwrap();
let result = process_file(temp.path()).unwrap();
assert_eq!(result, "HELLO WORLD");
}
}
fn main() {
println!("Run tests with: cargo test");
}Append Mode
use tempfile::NamedTempFile;
use std::io::{Write, Seek, SeekFrom};
fn main() -> std::io::Result<()> {
let mut temp = NamedTempFile::new()?;
// Write initial data
writeln!(temp, "Line 1")?;
// Seek to end for append
temp.seek(SeekFrom::End(0))?;
writeln!(temp, "Line 2")?;
// Read all
temp.seek(SeekFrom::Start(0))?;
let mut contents = String::new();
std::io::Read::read_to_string(&mut temp, &mut contents)?;
println!("{}", contents);
Ok(())
}Temporary File as Pipe
use tempfile::NamedTempFile;
use std::io::{Write, Read};
fn producer(temp: &mut NamedTempFile) -> std::io::Result<()> {
writeln!(temp, "Produced data")?;
temp.flush()?;
Ok(())
}
fn consumer(temp: &mut NamedTempFile) -> std::io::Result<String> {
use std::io::Seek;
temp.seek(std::io::SeekFrom::Start(0))?;
let mut data = String::new();
temp.read_to_string(&mut data)?;
Ok(data)
}
fn main() -> std::io::Result<()> {
let mut temp = NamedTempFile::new()?;
producer(&mut temp)?;
let result = consumer(&mut temp)?;
println!("Consumed: {}", result);
Ok(())
}Into File
use tempfile::NamedTempFile;
use std::fs::File;
use std::io::Write;
fn main() -> std::io::Result<()> {
let mut temp = NamedTempFile::new()?;
writeln!(temp, "Data")?;
// Convert to regular File (loses automatic cleanup!)
let file: File = temp.into_file();
// Now you manage the file manually
// Or keep the NamedTempFile for auto-cleanup
// let file: &File = temp.as_file();
Ok(())
}Multiple Files in Temp Dir
use tempfile::TempDir;
use std::fs::File;
use std::io::Write;
fn main() -> std::io::Result<()> {
let dir = TempDir::new()?;
// Create multiple files
for i in 0..5 {
let path = dir.path().join(format!("file{}.txt", i));
let mut file = File::create(path)?;
writeln!(file, "Content {}", i)?;
}
// List files
for entry in std::fs::read_dir(dir.path())? {
let entry = entry?;
println!("File: {:?}", entry.file_name());
}
// All deleted with dir on drop
Ok(())
}Cleanup on Error
use tempfile::NamedTempFile;
use std::io::Write;
fn write_config() -> std::io::Result<()> {
let mut temp = NamedTempFile::new()?;
// Write data
write!(temp, "config data")?;
// Simulate error - temp file is cleaned up
// return Err(std::io::Error::new(std::io::ErrorKind::Other, "oops"));
// Or persist on success
temp.persist("config.txt")?;
Ok(())
}
fn main() {
match write_config() {
Ok(()) => println!("Config saved"),
Err(e) => eprintln!("Error: {}", e),
}
}Large File Handling
use tempfile::NamedTempFile;
use std::io::Write;
fn main() -> std::io::Result<()> {
let mut temp = NamedTempFile::new()?;
// Write large data in chunks
for chunk in 0..100 {
let data = vec![chunk as u8; 1024]; // 1KB chunks
temp.write_all(&data)?;
}
println!("Wrote {} bytes", temp.as_file().metadata()?.len());
Ok(())
}Tempfile for Downloads
use tempfile::NamedTempFile;
use std::io::{Write, Copy};
// Simulated download function
fn download_to_temp(url: &str) -> std::io::Result<NamedTempFile> {
let mut temp = NamedTempFile::new()?;
// Simulate downloading
writeln!(temp, "Downloaded from: {}", url)?;
Ok(temp)
}
fn main() -> std::io::Result<()> {
let temp = download_to_temp("https://example.com/file")?;
// Process downloaded data
println!("Downloaded to: {:?}", temp.path());
// Or persist to final location
temp.persist("downloaded_file.txt")?;
Ok(())
}Atomic File Writes
use tempfile::NamedTempFile;
use std::io::Write;
use std::path::Path;
fn atomic_write(path: &Path, content: &str) -> std::io::Result<()> {
// Create temp file in same directory for atomic rename
let mut temp = NamedTempFile::new_in(path.parent().unwrap())?;
// Write content
write!(temp, "{}", content)?;
temp.flush()?;
// Atomic rename (persist)
temp.persist(path)?;
Ok(())
}
fn main() -> std::io::Result<()> {
atomic_write(std::path::Path::new("config.txt"), "config data")?;
println!("Wrote atomically");
Ok(())
}Getting File Handle
use tempfile::NamedTempFile;
use std::fs::File;
fn main() -> std::io::Result<()> {
let mut temp = NamedTempFile::new()?;
// Get reference to underlying File
let file: &File = temp.as_file();
println!("Metadata: {:?}", file.metadata()?);
// Get mutable reference
let file_mut: &mut File = temp.as_file_mut();
println!("Can modify: {:?}", file_mut);
Ok(())
}Error Handling
use tempfile::NamedTempFile;
fn main() {
match NamedTempFile::new() {
Ok(temp) => {
println!("Created: {:?}", temp.path());
}
Err(e) => {
eprintln!("Failed to create temp file: {}", e);
// Common errors:
// - No permission
// - Disk full
// - Invalid path
}
}
}TempDir with IntoPath
use tempfile::TempDir;
use std::path::PathBuf;
fn main() -> std::io::Result<()> {
let temp_dir = TempDir::new()?;
// Get path without keeping TempDir alive
// WARNING: No automatic cleanup!
let path: PathBuf = temp_dir.into_path();
println!("Path: {:?}", path);
// Now you must clean up manually
// std::fs::remove_dir_all(&path)?;
Ok(())
}Comparing Temp File Types
use tempfile::{tempfile, NamedTempFile, SpooledTempFile};
fn main() -> std::io::Result<()> {
// tempfile() - Anonymous, no path, most secure
let anon = tempfile()?;
// No .path() method - can't access by path
// NamedTempFile - Has path, can be reopened
let named = NamedTempFile::new()?;
println!("Named path: {:?}", named.path());
// SpooledTempFile - In memory up to limit
let spooled = SpooledTempFile::new(1024);
// No .path() - may or may not be on disk
println!("Three types created");
Ok(())
}Summary
Tempfile Key Imports:
use tempfile::{NamedTempFile, TempDir, tempfile, tempdir, SpooledTempFile, Builder};Main Types:
| Type | Description | Auto-Cleanup |
|---|---|---|
NamedTempFile |
Temp file with path | Yes |
TempDir |
Temporary directory | Yes |
tempfile() |
Anonymous temp file | Yes |
SpooledTempFile |
In-memory with disk spill | Yes |
Builder Pattern:
let temp = Builder::new()
.prefix("app_")
.suffix(".tmp")
.tempfile()?;Key Methods:
// NamedTempFile
temp.path(); // Get path
temp.as_file(); // Get &File
temp.persist(path); // Keep file permanently
// TempDir
dir.path(); // Get path
dir.into_path(); // Keep dir (no cleanup)Key Points:
- Files/dirs auto-delete on drop
- Use
persist()to keep files - Use
new_in()for custom location tempfile()is most secure (anonymous)SpooledTempFilefor memory efficiency- Great for tests and atomic writes
- Cleanup happens even on panic
Common Patterns:
- Testing file I/O:
let temp = NamedTempFile::new()?;
// use temp.path() in tests- Atomic writes:
let temp = NamedTempFile::new_in(dir)?;
temp.persist(final_path)?;- Large data buffering:
let mut spool = SpooledTempFile::new(max_memory);