Loading page…
Rust walkthroughs
Loading page…
The tempfile crate provides secure, cross-platform temporary file and directory handling. It automatically generates unique names, handles cleanup when the tempfile goes out of scope, and supports both named and anonymous temporary files. Temporary files are essential for testing, caching intermediate results, and processing data that shouldn't persist.
Key types:
NamedTempFile — temporary file with a visible path, auto-deleted on dropTempDir — temporary directory, auto-deleted on droptempfile() — anonymous temporary file (no path, more secure)tempdir() — temporary directory in system temp locationSpooledTempFile — in-memory buffer that spills to disk when largeTempfile handles platform differences automatically and provides secure file permissions.
# Cargo.toml
[dependencies]
tempfile = "3"use tempfile::NamedTempFile;
use std::io::{Write, Read};
fn main() -> std::io::Result<()> {
// Create a named temporary file
let mut temp_file = NamedTempFile::new()?;
// Write to it
writeln!(temp_file, "Hello, temporary file!")?;
// Get the path
println!("Temp file path: {:?}", temp_file.path());
// 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(())
}use tempfile::{NamedTempFile, tempfile};
use std::io::{Write, Read, Seek, SeekFrom};
fn main() -> std::io::Result<()> {
// Anonymous temporary file (no path on filesystem)
let mut anon = tempfile()?;
writeln!(anon, "Anonymous temp file")?;
println!("Anonymous temp file has no path");
// File is deleted immediately after creation on Unix
// It exists only as an open file handle
// Named temporary file (has a path)
let mut named = NamedTempFile::new()?;
writeln!(named, "Named temp file")?;
println!("Named temp file path: {:?}", named.path());
// File is deleted when `named` is dropped
// Named temp file with custom prefix/suffix
let mut custom = NamedTempFile::with_prefix("myapp_")?;
writeln!(custom, "Custom named temp")?;
println!("Custom temp: {:?}", custom.path());
let mut custom2 = NamedTempFile::with_suffix(".dat")?;
writeln!(custom2, "With suffix")?;
println!("Suffixed temp: {:?}", custom2.path());
let mut custom3 = NamedTempFile::with_prefix_and_suffix("myapp_", ".tmp")?;
writeln!(custom3, "Both prefix and suffix")?;
println!("Full custom: {:?}", custom3.path());
// Named temp file in a specific directory
let mut in_dir = NamedTempFile::new_in("./")?;
writeln!(in_dir, "Temp file in current directory")?;
println!("In current dir: {:?}", in_dir.path());
Ok(())
}use tempfile::{tempdir, TempDir};
use std::io::Write;
use std::fs::{self, File};
fn main() -> std::io::Result<()> {
// Create a temporary directory
let temp_dir = tempdir()?;
println!("Temp directory: {:?}", 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 directory")?;
// Create nested structure
let nested_dir = temp_dir.path().join("nested").join("deep");
fs::create_dir_all(&nested_dir)?;
let nested_file = nested_dir.join("nested_data.txt");
let mut file = File::create(&nested_file)?;
writeln!(file, "Nested data")?;
// List contents
println!("Contents of temp dir:");
for entry in fs::read_dir(temp_dir.path())? {
let entry = entry?;
println!(" {:?}", entry.path());
}
// Directory is deleted when temp_dir goes out of scope
println!("Directory exists: {}", temp_dir.path().exists());
Ok(())
// temp_dir is dropped here, directory is deleted
}
// TempDir with custom settings
fn custom_temp_dir() -> std::io::Result<()> {
// With prefix
let dir = tempfile::Builder::new()
.prefix("myapp_")
.tempdir()?;
println!("Prefixed dir: {:?}", dir.path());
// In specific location
let dir = tempfile::Builder::new()
.prefix("myapp_")
.tempdir_in("./")?;
println!("In current dir: {:?}", dir.path());
// With random suffix length
let dir = tempfile::Builder::new()
.prefix("myapp_")
.rand_bytes(12) // Longer random suffix
.tempdir()?;
println!("Longer suffix: {:?}", dir.path());
Ok(())
}use tempfile::NamedTempFile;
use std::io::{Write, Read};
use std::path::PathBuf;
fn main() -> std::io::Result<()> {
// Create a temp file
let mut temp = NamedTempFile::new()?;
writeln!(temp, "Important data")?;
println!("Temp file: {:?}", temp.path());
// Persist the file to a permanent location
let persisted_path = PathBuf::from("./persisted_data.txt");
let mut persisted = temp.persist(&persisted_path)?;
println!("Persisted to: {:?}", persisted_path);
println!("Temp file no longer exists at original path");
// Can still use the file handle
persisted.seek(std::io::SeekFrom::Start(0))?;
let mut contents = String::new();
persisted.read_to_string(&mut contents)?;
println!("Contents: {}", contents);
// Clean up persisted file
std::fs::remove_file(&persisted_path)?;
// Alternative: keep the temp file and get its path
let temp2 = NamedTempFile::new()?;
let (file, path) = temp2.into_parts();
println!("File handle and path separated: {:?}", path);
// Now you're responsible for cleaning up the file
std::fs::remove_file(&path)?;
Ok(())
}
// Persist with error handling
fn persist_with_fallback() -> std::io::Result<()> {
let temp = NamedTempFile::new()?;
let target_path = PathBuf::from("./target_file.txt");
// Try to persist, handle error
match temp.persist(&target_path) {
Ok(_file) => {
println!("Successfully persisted to {:?}", target_path);
}
Err(e) => {
// Error contains both the error and the original temp file
println!("Failed to persist: {}", e.error);
println!("Temp file still at: {:?}", e.file.path());
// You can retry or handle the temp file
}
}
Ok(())
}use tempfile::SpooledTempFile;
use std::io::{Write, Read, Seek, SeekFrom};
fn main() -> std::io::Result<()> {
// Create a spooled temp file (in-memory until max size)
let mut spool = SpooledTempFile::new(100); // 100 bytes max in memory
// Write small amount (stays in memory)
write!(spool, "Small data")?;
if spool.is_in_memory() {
println!("Data is still in memory");
}
// Write more data (exceeds limit, spills to disk)
let large_data = "x".repeat(200);
write!(spool, "{}", large_data)?;
if !spool.is_in_memory() {
println!("Data has spilled to disk");
}
// Read back
spool.seek(SeekFrom::Start(0))?;
let mut contents = String::new();
spool.read_to_string(&mut contents)?;
println!("Read {} bytes", contents.len());
// Spooled files are also cleaned up on drop
Ok(())
}
// Spooled file with custom max size
fn spooled_example() -> std::io::Result<()> {
// 1MB in-memory limit
let mut spool = SpooledTempFile::new(1024 * 1024);
// Write 500KB (stays in memory)
let data_500kb = vec![0u8; 500 * 1024];
spool.write_all(&data_500kb)?;
println!("After 500KB: in_memory = {}", spool.is_in_memory());
// Write another 500KB (total 1MB, still in memory)
spool.write_all(&data_500kb)?;
println!("After 1MB: in_memory = {}", spool.is_in_memory());
// Write more (spills to disk)
let extra = vec![0u8; 1024];
spool.write_all(&extra)?;
println!("After overflow: in_memory = {}", spool.is_in_memory());
// Roll back to memory (only if under limit)
let mut spool2 = SpooledTempFile::new(100);
write!(spool2, "Small")?;
let rolled_back = spool2.rollback_to_memory();
println!("Rolled back: {}", rolled_back);
Ok(())
}use tempfile::{NamedTempFile, TempDir};
use std::io::{Write, Read};
use std::fs;
// Test helper to create temp file with content
fn create_temp_file(content: &str) -> std::io::Result<NamedTempFile> {
let mut file = NamedTempFile::new()?;
write!(file, "{}", content)?;
Ok(file)
}
// Test that needs a file
#[test]
fn test_read_file() {
let temp = create_temp_file("test content").unwrap();
let path = temp.path();
let content = fs::read_to_string(path).unwrap();
assert_eq!(content, "test content");
// temp is cleaned up at end of test
}
// Test that needs a directory
#[test]
fn test_directory_operations() {
let dir = TempDir::new().unwrap();
// Create test files
fs::write(dir.path().join("a.txt"), "content a").unwrap();
fs::write(dir.path().join("b.txt"), "content b").unwrap();
// Run test
let entries: Vec<_> = fs::read_dir(dir.path())
.unwrap()
.map(|e| e.unwrap().path())
.collect();
assert_eq!(entries.len(), 2);
}
// Test with multiple temp files
#[test]
fn test_multiple_temp_files() {
let dir = TempDir::new().unwrap();
let file1 = NamedTempFile::new_in(dir.path()).unwrap();
let file2 = NamedTempFile::new_in(dir.path()).unwrap();
// Files are cleaned up automatically
assert!(file1.path().exists());
assert!(file2.path().exists());
}
// Test with fixture
struct TestFixture {
temp_dir: TempDir,
config_path: std::path::PathBuf,
data_path: std::path::PathBuf,
}
impl TestFixture {
fn new() -> std::io::Result<Self> {
let temp_dir = TempDir::new()?;
let config_path = temp_dir.path().join("config.json");
let data_path = temp_dir.path().join("data.bin");
// Create initial files
fs::write(&config_path, r#"{"setting": "value"}"#)?;
fs::write(&data_path, vec![0u8; 100])?;
Ok(Self {
temp_dir,
config_path,
data_path,
})
}
fn config_path(&self) -> &std::path::Path {
&self.config_path
}
fn data_path(&self) -> &std::path::Path {
&self.data_path
}
}
#[test]
fn test_with_fixture() {
let fixture = TestFixture::new().unwrap();
// Use fixture paths
assert!(fixture.config_path().exists());
assert!(fixture.data_path().exists());
// temp_dir cleaned up when fixture is dropped
}use tempfile::{NamedTempFile, TempDir, tempdir};
use std::io::Write;
use std::fs;
fn main() -> std::io::Result<()> {
// Automatic cleanup (default)
{
let temp = NamedTempFile::new()?;
writeln!(temp, "Auto-cleaned")?;
println!("Temp file: {:?}", temp.path());
// temp is dropped here, file is deleted
}
// Manual cleanup control
let temp = NamedTempFile::new()?;
writeln!(temp, "Manual control")?;
// Close without deleting
let (file, path) = temp.into_parts();
drop(file); // Close file handle, but file remains
println!("File still exists: {}", path.exists());
// Manual cleanup
fs::remove_file(&path)?;
println!("File manually deleted");
// Temporary directory cleanup
{
let dir = tempdir()?;
fs::write(dir.path().join("test.txt"), "content")?;
println!("Dir: {:?}", dir.path());
// dir is dropped here, directory and contents deleted
}
// Keep directory by converting to path
let dir = tempdir()?;
let kept_path = dir.into_path();
fs::write(kept_path.join("persistent.txt"), "stays")?;
println!("Kept path: {:?}", kept_path);
// Directory is NOT deleted
// Clean up manually
fs::remove_dir_all(&kept_path)?;
Ok(())
}
// Cleanup on close behavior
fn cleanup_behavior() -> std::io::Result<()> {
// Create temp file with close behavior
let mut temp = NamedTempFile::new()?;
writeln!(temp, "Testing cleanup")?;
// Default: file deleted on drop
println!("Path: {:?}", temp.path());
// Persist to keep it
let persisted = temp.persist("./kept.txt")?;
println!("Persisted to ./kept.txt");
// Clean up
fs::remove_file("./kept.txt")?;
Ok(())
}use tempfile::{NamedTempFile, TempDir};
use std::io::Write;
use std::path::Path;
fn main() -> std::io::Result<()> {
// In current directory
let temp_here = NamedTempFile::new_in(".")?;
println!("In current dir: {:?}", temp_here.path());
// In specific directory
let custom_dir = Path::new("./temp_files");
fs_extra::create_dir_all(custom_dir)?;
let temp_custom = NamedTempFile::new_in(custom_dir)?;
println!("In custom dir: {:?}", temp_custom.path());
// Temp directory in specific location
let dir_in_custom = TempDir::new_in(custom_dir)?;
println!("Dir in custom: {:?}", dir_in_custom.path());
// Clean up
std::fs::remove_dir_all(custom_dir)?;
Ok(())
}
mod fs_extra {
use std::fs;
use std::path::Path;
use std::io;
pub fn create_dir_all(path: &Path) -> io::Result<()> {
fs::create_dir_all(path)
}
}
// System temp directory
fn system_temp() -> std::io::Result<()> {
// Get system temp directory
let temp_root = std::env::temp_dir();
println!("System temp dir: {:?}", temp_root);
// Create temp file there (default behavior)
let temp = NamedTempFile::new()?;
assert!(temp.path().starts_with(&temp_root));
// Temp dir in system temp (default)
let dir = TempDir::new()?;
assert!(dir.path().starts_with(&temp_root));
Ok(())
}use tempfile::NamedTempFile;
use std::io::Write;
#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;
fn main() -> std::io::Result<()> {
// tempfile creates files with secure permissions (0600 on Unix)
let mut temp = NamedTempFile::new()?;
writeln!(temp, "Sensitive data")?;
println!("Temp file: {:?}", temp.path());
// On Unix, check permissions
#[cfg(unix)]
{
let metadata = std::fs::metadata(temp.path())?;
let mode = metadata.permissions().mode();
println!("Permissions: {:o}", mode & 0o777);
// Should be 600 (rw-------)
}
// tempfile() creates anonymous files
// These are immediately unlinked on Unix, so they don't
// appear in directory listings and are automatically cleaned
// up by the OS when the file handle is closed
let mut anon = tempfile::tempfile()?;
writeln!(anon, "Anonymous file");
// No path, more secure for sensitive data
Ok(())
}
// Secure temp file for sensitive data
fn secure_temp() -> std::io::Result<()> {
// Use anonymous tempfile for sensitive data
// No path on filesystem, can't be accessed by other processes
let mut secure = tempfile::tempfile()?;
writeln!(secure, "Password or secret")?;
// File never had a name, never appeared in filesystem
// Automatically cleaned up by OS
Ok(())
}use tempfile::{NamedTempFile, TempDir, tempdir};
use std::io::{Write, Read, BufWriter, BufReader};
use std::fs::{self, File};
use std::process::Command;
// Atomic file write pattern
fn atomic_write(path: &std::path::Path, content: &str) -> std::io::Result<()> {
// Write to temp file first
let mut temp = NamedTempFile::new_in(path.parent().unwrap_or(std::path::Path::new(".")))?;
write!(temp, "{}", content)?;
// Sync to disk
temp.flush()?;
// Atomically rename to target
temp.persist(path)?;
Ok(())
}
// Process file with external command
fn process_with_command(input: &str) -> std::io::Result<String> {
// Create temp file for input
let mut input_file = NamedTempFile::new()?;
write!(input_file, "{}", input)?;
// Create temp file for output
let output_file = NamedTempFile::new()?;
let output_path = output_file.path().to_owned();
// Run external command (example: cat)
let status = Command::new("cat")
.arg(input_file.path())
.output()?;
// Read result
let result = String::from_utf8_lossy(&status.stdout).to_string();
Ok(result)
}
// Multi-step processing with temp directory
fn multi_step_processing(data: &[u8]) -> std::io::Result<Vec<u8>> {
let temp_dir = tempdir()?;
// Step 1: Write input
let input_path = temp_dir.path().join("input.bin");
fs::write(&input_path, data)?;
// Step 2: Process (simulate)
let intermediate_path = temp_dir.path().join("intermediate.bin");
let intermediate: Vec<u8> = data.iter().map(|b| b.wrapping_add(1)).collect();
fs::write(&intermediate_path, &intermediate)?;
// Step 3: Final output
let output_path = temp_dir.path().join("output.bin");
let output: Vec<u8> = intermediate.iter().map(|b| b.wrapping_add(1)).collect();
fs::write(&output_path, &output)?;
// Read result
let result = fs::read(&output_path)?;
// All temp files cleaned up automatically
Ok(result)
}
// Buffered writing to temp file
fn buffered_temp_write(lines: &[&str]) -> std::io::Result<String> {
let temp = NamedTempFile::new()?;
// Use buffered writer for efficiency
let mut writer = BufWriter::new(&temp);
for line in lines {
writeln!(writer, "{}", line)?;
}
writer.flush()?;
// Read back
let mut reader = BufReader::new(&temp);
let mut contents = String::new();
reader.read_to_string(&mut contents)?;
Ok(contents)
}
// Temporary workspace for file operations
struct Workspace {
dir: TempDir,
}
impl Workspace {
fn new() -> std::io::Result<Self> {
Ok(Self {
dir: tempdir()?,
})
}
fn create_file(&self, name: &str, content: &str) -> std::io::Result<std::path::PathBuf> {
let path = self.dir.path().join(name);
fs::write(&path, content)?;
Ok(path)
}
fn path(&self) -> &std::path::Path {
self.dir.path()
}
}
fn main() -> std::io::Result<()> {
// Atomic write
atomic_write(std::path::Path::new("./test_atomic.txt"), "Hello, atomic!")?;
println!("Atomic write complete");
fs::remove_file("./test_atomic.txt")?;
// Process with command
let result = process_with_command("test input")?;
println!("Processed: {}", result);
// Multi-step processing
let data = vec![1, 2, 3, 4, 5];
let processed = multi_step_processing(&data)?;
println!("Processed data: {:?}", processed);
// Buffered temp write
let lines = vec!["line 1", "line 2", "line 3"];
let content = buffered_temp_write(&lines)?;
println!("Buffered content:\n{}", content);
// Workspace
let ws = Workspace::new()?;
ws.create_file("config.txt", "setting=value")?;
ws.create_file("data.txt", "important data")?;
println!("Workspace: {:?}", ws.path());
Ok(())
}use tempfile::{NamedTempFile, PersistError};
use std::io;
use std::path::PathBuf;
fn handle_tempfile_errors() -> Result<(), Box<dyn std::error::Error>> {
// Create temp file
let temp = NamedTempFile::new()?; // io::Error
// Persist with error handling
let target = PathBuf::from("/root/protected.txt"); // May fail due to permissions
match temp.persist(&target) {
Ok(_file) => {
println!("Successfully persisted");
}
Err(PersistError { error, file }) => {
// error: the io::Error that occurred
// file: the NamedTempFile you can retry with
eprintln!("Failed to persist: {}", error);
println!("Temp file still at: {:?}", file.path());
// Retry with different location
let fallback = PathBuf::from("./fallback.txt");
file.persist(&fallback)?;
}
}
Ok(())
}
// Custom error type
#[derive(Debug)]
enum TempFileError {
Io(io::Error),
Persist(String),
}
impl From<io::Error> for TempFileError {
fn from(err: io::Error) -> Self {
TempFileError::Io(err)
}
}
impl From<PersistError> for TempFileError {
fn from(err: PersistError) -> Self {
TempFileError::Persist(format!("{}: {:?}", err.error, err.file.path()))
}
}
fn safe_temp_operation() -> Result<NamedTempFile, TempFileError> {
let mut temp = NamedTempFile::new()?;
writeln!(temp, "data")?;
Ok(temp)
}tempfile() for anonymous temporary files (no filesystem path)NamedTempFile::new() for temporary files with a pathtempdir() for temporary directoriesNamedTempFile::with_prefix("myapp_") for custom filename prefixNamedTempFile::new_in(dir) to create in specific directorytemp.persist(path) to keep a temp file permanentlytemp.into_parts() to get file handle and path separatelySpooledTempFile::new(max_bytes) for in-memory files that spill to diskspool.is_in_memory() to see if data is still in RAMtempfile::Builder for advanced configurationtempfile()) are more secure as they have no pathpersist() for atomic writesPersistError contains both the error and the original temp file for retry