🎓 A1Learner
🔍

Rust Paths

Path and PathBuf are Rust's types for file paths. A path names a file, like "story.txt".

Build a path piece by piece

push(part) extends a PathBuf in place, one piece at a time:

let mut path = PathBuf::from("reports");
path.push("2026");
path.push("sales.txt");
println!("{}", path.display());

Join in one call

join adds one piece with the separator this computer uses — never glue pieces together with / or \ by hand:

let path = Path::new("reports").join("sales.txt");
println!("{}", path.display());

Take a path apart

file_name() gives the last piece. parent() gives everything before it. extension() gives the part after the final dot. And file_stem() gives the name without its extension. display() prints a path. to_str() — when you truly need text — returns an Option. The name might not be UTF-8. to_string_lossy() is the forgiving printer used here:

let path = Path::new("reports/2026/sales.txt");
println!("name = {}", path.file_name().unwrap().to_string_lossy());
println!("stem = {}", path.file_stem().unwrap().to_string_lossy());
println!("ext = {}", path.extension().unwrap().to_string_lossy());
println!("parent = {}", path.parent().unwrap().display());

Take the last piece back off

pop() removes the piece that push added last:

let mut path = PathBuf::from("reports/2026/sales.txt");
path.pop();
println!("{}", path.display());

Ask where the program is running

env::current_dir() gives the folder the program is running from, as a PathBuf. It returns a Result, because the folder could have been deleted. env::set_current_dir(path) moves the program to a different folder:

let here = env::current_dir().unwrap();

Take a path as a parameter

A function can take impl AsRef<Path> as its parameter type. One signature then accepts text, a &Path, or a PathBuf. Callers pass what they already have:

fn name_of(path: impl AsRef<Path>) -> String {
    let path = path.as_ref();
    path.file_name().unwrap().to_string_lossy().into_owned()
}
println!("{}", name_of("reports/sales.txt"));
println!("{}", name_of(PathBuf::from("data.bin")));

Example

use std::path::PathBuf;
fn main() {
    let mut path = PathBuf::from("reports");
    path.push("2026");
    path.push("sales.txt");
    println!("full = {}", path.display());
    println!("name = {}", path.file_name().unwrap().to_string_lossy());
    println!("stem = {}", path.file_stem().unwrap().to_string_lossy());
    println!("ext = {}", path.extension().unwrap().to_string_lossy());
    println!("parent = {}", path.parent().unwrap().display());
}

Key points

  • Use std::path::Path for a borrowed path and std::path::PathBuf for an owned one — the same pairing as &str and String
  • A Path is not plain text inside.
  • It is stored as an OsString.
  • So it can hold file names that are not valid UTF-8 text.
  • Use &Path or impl AsRef<Path> for path parameters.
  • One function then accepts text, Path, and PathBuf arguments alike.
  • Plain file names use the same current-folder rule.

In this example, the program runs on a Linux or Mac terminal, where the separator is /. PathBuf::from("reports") starts an owned path holding one piece. push adds 2026, then sales.txt — each push uses the right separator by itself. file_name() picks out the last piece, sales.txt. file_stem() drops the ending, giving sales, and extension() keeps only the ending, txt. parent() gives everything before the name: reports/2026. On Windows the same code prints backslashes instead — the pieces and answers stay the same.

Note

Path::new(".") means the folder the program runs from.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

What does extension() return for the path reports/sales.txt?