How to get the current working directory
Short answer: std::env::current_dir() returns the process’s working directory as a PathBuf, wrapped in io::Result. It’s the directory the shell was in when the program started — not the directory your binary lives in — and every relative path your program opens is resolved against it.
From a fresh project — cargo new cwd-demo, then replace the contents of src/main.rs with the block below and run cargo run:
The directory the program was launched from
Section titled “The directory the program was launched from”use std::env;use std::path::PathBuf;
fn main() -> std::io::Result<()> { let cwd: PathBuf = env::current_dir()?; println!("cwd: {}", cwd.display()); println!("config would be: {}", cwd.join("config.toml").display()); Ok(())}cwd: /private/tmp/cwd-democonfig would be: /private/tmp/cwd-demo/config.tomlThe exact path depends on where the program was launched from, so the one you see will be your own project directory. Two things are worth noticing about the value. It’s absolute, no matter how the program was invoked. And it comes back as a PathBuf, not a string — which is the useful part, because .join() on a PathBuf builds a sibling path portably instead of concatenating strings and hoping the separator is right. (cwd.display() is just how you print one.)
The Result is not decoration either. Most of the time it’s Ok, but a working directory can genuinely fail to resolve — the last section shows it happening.
It follows the shell, not the binary
Section titled “It follows the shell, not the binary”The binary built above sits at target/debug/cwd-demo. Watch what it prints when launched from different places (bash on macOS; your paths will be different):
$ cargo run -qcwd: /private/tmp/cwd-demo$ ./target/debug/cwd-democwd: /private/tmp/cwd-demo$ cd target$ ./debug/cwd-democwd: /private/tmp/cwd-demo/targetOne file on disk — target/debug/cwd-demo — printing two different answers. cargo run launches the binary from the directory you invoke cargo in, so both calls from the project root print /private/tmp/cwd-demo; stepping into target first changes the answer to /private/tmp/cwd-demo/target. Nothing was passed to the program: the working directory is process state the OS copies in at launch.
This is why the mismatch matters in practice. fs::read_to_string("config.toml") looks in the working directory, not next to the executable. A program that finds its config under cargo run fails with NotFound when launched from somewhere else — the file didn’t move, the answer to “where am I?” did. If what you actually want is the executable’s own location, that’s a different question with a different answer, and it gets its own page.
When it’s an Err
Section titled “When it’s an Err”The working directory can stop existing. Launch the binary from a directory, delete the directory, and ask again (bash on macOS):
$ mkdir /tmp/vanishing && cd /tmp/vanishing$ /tmp/cwd-demo/target/debug/cwd-democwd: /private/tmp/vanishing$ rm -rf /tmp/vanishing$ /tmp/cwd-demo/target/debug/cwd-demoError: Os { code: 2, kind: NotFound, message: "No such file or directory" }The OS can’t produce a path for a directory that no longer exists, so current_dir() returns Err with kind: NotFound, and the ? in main turns that into the Error: line and an exit code of 1. It’s rare — most programs never hit it — but it’s why the return type isn’t a plain PathBuf.
Related
Section titled “Related”- How to create a directory and its parents — relative paths there resolve against this directory
- How to read a file into a String —
fs::read_to_string("config.toml")is working-directory-relative too - Reading and writing files — the rest of the chapter