File System Operations

Path and PathBuf

std::path::Path (an unsized borrowed path slice, analogous to str) and std::path::PathBuf (an owned, growable heap-allocated path, analogous to String) are the standard types for representing filesystem paths in Rust.

Why Paths Use OsStr / OsString Instead of str / String

On real operating systems, not every valid filesystem path is valid UTF-8:

  • On Unix (Linux, FreeBSD), filenames are arbitrary sequences of non-null bytes ([u8] without 0x00), which may contain non-UTF-8 byte sequences.
  • On Windows, filenames are sequences of potentially unpaired 16-bit UTF-16 code units ([u16] / WTF-8).

Therefore, Path is a #[repr(transparent)] wrapper around std::ffi::OsStr, and PathBuf wraps std::ffi::OsString, guaranteeing that Rust programs can open, copy, and traverse every file on disk without failing on non-UTF-8 filenames!

String vs. OS-String vs. Path Type Matrix:

               Borrowed Slice (`!Sized`)      Owned Heap Buffer (`Sized`)
               -------------------------      ---------------------------
  UTF-8 Text:  `str`   (&str)        <----->  `String`
                 |                              |
                 v (Infallible `AsRef`)         v
  OS String:   `OsStr` (&OsStr)      <----->  `OsString`
                 |                              |
                 v (`#[repr(transparent)]`)     v
  Filesystem:  `Path`  (&Path)       <----->  `PathBuf` (`Deref<Target = Path>`)

Converting Between Path and Strings (to_str vs. to_string_lossy vs. display)

Because a Path might contain non-UTF-8 bytes, there is no infallible .as_str() -> &str method on Path. Instead, Path provides three conversion methods depending on your needs:

Method on &Path Return Type When to Use
.display() std::path::Display<'_> Formatting paths in user/log messages (println!("opened {}", path.display())) — zero allocation!
.to_str() Option<&str> Zero-copy conversion when your application strictly requires valid UTF-8 (None if path contains non-UTF-8 bytes)
.to_string_lossy() Cow<'_, str> Returns Cow::Borrowed(&str) (zero allocation!) if valid UTF-8, or Cow::Owned(String) with replacement characters `` if non-UTF-8
.as_os_str() &OsStr Passing paths directly to std::process::Command or OS APIs without losing non-UTF-8 bytes
use std::path::{Path, PathBuf};

fn inspect_path(path: &Path) {
    println!("display():       {}", path.display());
    println!("to_str():        {:?}", path.to_str());
    println!("is_absolute():   {}", path.is_absolute());
}

fn main() {
    let borrowed: &Path = Path::new("/var/log/syslog");
    let mut owned: PathBuf = PathBuf::from("/var/log");
    owned.push("nginx/access.log");

    // `PathBuf` deref-coerces to `&Path` automatically:
    inspect_path(borrowed);
    inspect_path(&owned);
}
display():       /var/log/syslog
to_str():        Some("/var/log/syslog")
is_absolute():   true
display():       /var/log/nginx/access.log
to_str():        Some("/var/log/nginx/access.log")
is_absolute():   true