Path Operations
Overview
The std::path module provides cross-platform path manipulation. Path and PathBuf handle filesystem paths safely across Windows, Unix, and other platforms.
Code Example
#![allow(unused)]
fn main() {
use std::path::Path;
let path = Path::new("example.txt");
println!("Path exists: {}", path.exists());
println!("Path: {}", path.display());
}
Explanation
Path::new(): Creates a path reference from a stringexists(): Checks if path exists in filesystemdisplay(): Formats path for display (handles encoding)
Key Concepts
Path vs PathBuf
Path: Borrowed path slice (like&str)PathBuf: Owned path (likeString)
#![allow(unused)]
fn main() {
use std::path::{Path, PathBuf};
// Borrowed
let path: &Path = Path::new("/tmp/file.txt");
// Owned
let mut path_buf = PathBuf::from("/tmp");
path_buf.push("file.txt");
}
Path Components
#![allow(unused)]
fn main() {
let path = Path::new("/home/user/file.txt");
// File name
println!("{:?}", path.file_name()); // Some("file.txt")
// Extension
println!("{:?}", path.extension()); // Some("txt")
// Parent directory
println!("{:?}", path.parent()); // Some("/home/user")
// Components
for component in path.components() {
println!("{:?}", component);
}
}
Path Construction
#![allow(unused)]
fn main() {
let mut path = PathBuf::from("/home");
path.push("user");
path.push("file.txt");
// /home/user/file.txt
// Join paths
let path = Path::new("/home").join("user").join("file.txt");
}
Use Cases
- File Location: Build paths to files and directories
- Path Validation: Check if paths exist or are valid
- Path Parsing: Extract filename, extension, parent
- Cross-Platform: Write code that works on any OS
- Configuration: Handle user-provided paths safely
Best Practices
✅ Do:
- Use
PathandPathBufinstead of strings for paths - Use
join()to combine path components - Handle relative vs absolute paths appropriately
- Use
canonicalize()to resolve symlinks and..
❌ Don’t:
- Manually concatenate paths with
/or\\ - Assume path separators (use
std::path::MAIN_SEPARATOR) - Ignore path encoding issues (use
display()orto_string_lossy())
Common Operations
Check Path Type
#![allow(unused)]
fn main() {
use std::path::Path;
let path = Path::new("file.txt");
if path.is_file() {
println!("It's a file");
}
if path.is_dir() {
println!("It's a directory");
}
if path.is_symlink() {
println!("It's a symbolic link");
}
}
Path Manipulation
#![allow(unused)]
fn main() {
let path = PathBuf::from("/home/user/file.txt");
// Change extension
let new_path = path.with_extension("md");
// /home/user/file.md
// Change filename
let new_path = path.with_file_name("other.txt");
// /home/user/other.txt
// Remove filename
let mut path = path;
path.pop(); // Now: /home/user
}
Canonicalize Paths
#![allow(unused)]
fn main() {
// Resolve . and .. and symlinks
let canonical = Path::new("../file.txt").canonicalize()?;
println!("Absolute path: {}", canonical.display());
}
Common Patterns
Build Output Path
#![allow(unused)]
fn main() {
let input = Path::new("input.txt");
let output = input.with_extension("out");
}
Iterate Directory
#![allow(unused)]
fn main() {
use std::fs;
for entry in fs::read_dir(".")? {
let entry = entry?;
let path = entry.path();
println!("{}", path.display());
}
}
Safe Path Join
#![allow(unused)]
fn main() {
fn safe_join(base: &Path, user_path: &str) -> Option<PathBuf> {
let joined = base.join(user_path);
if joined.starts_with(base) {
Some(joined)
} else {
None // Prevent directory traversal
}
}
}
Related Features
- File I/O - Reading and writing files
- Environment Variables - Getting system paths