Current Working Directory
The current working directory (CWD) is the process-global base directory against which the operating system resolves all relative paths (such as "config/settings.toml" or "./data"). Rust inspects and modifies this directory via std::env::current_dir() and std::env::set_current_dir().
Process CWD: /srv/app
|
+-- File::open("config.toml") --> resolves to /srv/app/config.toml
+-- File::open("../shared/db.key") --> resolves to /srv/shared/db.key
+-- File::open("/etc/hosts") --> absolute path; ignores CWD
Inspecting the Current Working Directory
std::env::current_dir() queries the operating system (getcwd on Unix, GetCurrentDirectoryW on Windows) and returns io::Result<PathBuf>.
current_dir() can return an Err in two real-world situations:
- The directory in which the process is running was unlinked/deleted after the process entered it.
- The process lacks read or search permissions on one of the ancestor directories along the path.
use std::env;
use std::io;
fn main() -> io::Result<()> {
let cwd = env::current_dir()?;
println!("Current working directory is absolute: {}", cwd.is_absolute());
Ok(())
}
Current working directory is absolute: true
Changing the Working Directory
std::env::set_current_dir<P: AsRef<Path>>(path: P) changes the current working directory of the running process (chdir on Unix, SetCurrentDirectoryW on Windows).
use std::env;
use std::fs;
use std::io;
fn main() -> io::Result<()> {
let original_cwd = env::current_dir()?;
let work_dir = env::temp_dir().join("rust_ch51_cwd_demo");
fs::create_dir_all(&work_dir)?;
// Switch CWD to the temporary work directory
env::set_current_dir(&work_dir)?;
// Relative paths now resolve inside `work_dir`
fs::write("hello.txt", "Written via relative path\n")?;
assert!(work_dir.join("hello.txt").exists());
println!("Relative write confirmed inside work_dir");
// Restore original CWD before removing `work_dir`
env::set_current_dir(&original_cwd)?;
fs::remove_dir_all(&work_dir)?;
Ok(())
}
Relative write confirmed inside work_dir
Process-Global Scope and Concurrency Hazards
The current working directory is a process-wide kernel property, not a thread-local property. Calling env::set_current_dir on one thread immediately alters how relative paths are resolved on every other thread in the same process.
| Operation | Scope | Thread-Safe Recommendation |
|---|---|---|
env::set_current_dir(dir) |
Entire process (all threads) | Avoid in multi-threaded servers; keep an explicit base_dir: PathBuf and call base_dir.join(rel_path). |
Command::current_dir(dir) |
Only the spawned child process | Always use Command::current_dir when launching a subprocess that needs a different working directory. |
RAII Working Directory Guard for Single-Threaded Tools
In single-threaded CLI tools or build scripts that temporarily switch directories, an RAII guard ensures the original directory is restored even on early return:
use std::env;
use std::io;
use std::path::{Path, PathBuf};
struct DirGuard {
previous: PathBuf,
}
impl DirGuard {
fn enter(target: impl AsRef<Path>) -> io::Result<Self> {
let previous = env::current_dir()?;
env::set_current_dir(target)?;
Ok(Self { previous })
}
}
impl Drop for DirGuard {
fn drop(&mut self) {
let _ = env::set_current_dir(&self.previous);
}
}
fn main() -> io::Result<()> {
let start = env::current_dir()?;
{
let _guard = DirGuard::enter(env::temp_dir())?;
assert_ne!(env::current_dir()?, start);
}
assert_eq!(env::current_dir()?, start);
println!("Restored original working directory via DirGuard");
Ok(())
}
Restored original working directory via DirGuard