Executable Paths
An executable path identifies the file system location of the currently running binary or of external programs located along the system PATH. Rust provides std::env::current_exe() to locate the running binary and std::env::split_paths() to search for external executables.
std::env::current_exe() vs env::args().next()
Programmers coming from C often look at argv[0] (env::args().next()) to find the binary's location, but argv[0] is merely the string passed by the parent process (such as "cargo" or a relative path "./app"). std::env::current_exe() queries the operating system kernel directly (/proc/self/exe on Linux, _NSGetExecutablePath on macOS, GetModuleFileNameW on Windows).
| Aspect | std::env::current_exe() |
std::env::args_os().next() (argv[0]) |
|---|---|---|
| Source | OS Kernel process metadata | Arbitrary string passed by parent caller |
| Path Format | Absolute PathBuf |
May be relative (./app), bare name (app), or forged |
| CWD Sensitivity | Unaffected by env::set_current_dir |
Relative argv[0] breaks if CWD changes |
| Symlink Behavior | Platform-dependent (often resolves symlinks on Linux) | Preserves the symlink name used to invoke the binary |
Locating Bundled Assets Relative to the Binary
Desktop applications and CLI distributions often store static assets, plugins, or default configuration files in a directory adjacent to the executable (<install_dir>/bin/app and <install_dir>/share/assets):
use std::env;
use std::fs;
use std::io;
use std::path::PathBuf;
fn executable_dir() -> io::Result<PathBuf> {
let exe_path = env::current_exe()?;
// Canonicalize to resolve any intermediate symlinks
let canonical = fs::canonicalize(&exe_path).unwrap_or(exe_path);
canonical
.parent()
.map(PathBuf::from)
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "executable has no parent dir"))
}
fn main() -> io::Result<()> {
let dir = executable_dir()?;
println!("Executable resides in existing dir: {}", dir.is_dir());
Ok(())
}
Executable resides in existing dir: true
Security Notice for current_exe()
As noted in the standard library documentation, the path returned by env::current_exe() should not be trusted for security-sensitive privilege checks. On some operating systems, a hard link or unlinked-and-replaced binary can cause current_exe() to point to a different file than the image mapped in memory.
Searching PATH for an External Executable
To find where an external command (like git or rustc) resides on disk—similar to the Unix which utility—combine env::var_os("PATH"), env::split_paths, and env::consts::EXE_EXTENSION:
use std::env;
use std::path::PathBuf;
fn find_in_path(program: &str) -> Option<PathBuf> {
let path_var = env::var_os("PATH")?;
for dir in env::split_paths(&path_var) {
let candidate = dir.join(program);
if candidate.is_file() {
return Some(candidate);
}
// On Windows, also check with ".exe" if no extension was supplied
if !env::consts::EXE_EXTENSION.is_empty() && candidate.extension().is_none() {
let with_ext = candidate.with_extension(env::consts::EXE_EXTENSION);
if with_ext.is_file() {
return Some(with_ext);
}
}
}
None
}
fn main() {
// `rustc` or `sh` is typically available in PATH
let found = find_in_path("rustc").or_else(|| find_in_path("sh"));
println!("Located system executable: {}", found.is_some());
}
Located system executable: true