#270 Jul 20, 2026

270. Path::try_exists — exists() Can't Tell "Missing" From "Couldn't Check"

path.exists() returns false for a file that’s missing — and for a file that’s right there behind a directory you can’t read. try_exists() finally separates the two.

Path::exists() is implemented as “did fs::metadata succeed?”. Any failure — permission denied on a parent directory, an I/O error, an interrupted syscall — collapses into false:

1
2
3
4
5
6
let cfg = Path::new("/etc/app/config.toml");

if !cfg.exists() {
    // Missing? Or unreadable? No way to know.
    write_default_config(cfg)?;
}

If the app lacks permission to look inside /etc/app, this code concludes the config is absent and happily tries to overwrite it — or silently skips loading settings that exist. The error didn’t go away; it was converted into wrong control flow.

Path::try_exists() (stable since 1.63) returns io::Result<bool>, so “no” and “don’t know” are different answers:

1
2
3
4
5
match cfg.try_exists() {
    Ok(true)  => load_config(cfg)?,
    Ok(false) => write_default_config(cfg)?,
    Err(e)    => return Err(e.into()), // surface it
}

Ok(false) now means the path is definitely absent — the lookup succeeded and found nothing. Everything else is an Err you can log, retry, or propagate with ?.

Two details worth knowing:

  • Broken symlinks report Ok(false)try_exists() follows symlinks, so a link pointing at nothing counts as “the target doesn’t exist”. Use symlink_metadata if the link itself is what you care about.
  • TOCTOU still applies — any exists-then-act sequence can race with other processes. For “create only if absent”, skip the check entirely and use OpenOptions::new().create_new(true) (bite 169), which makes the filesystem decide atomically.

Same theme as file_type() in bite 269: the std methods returning plain bool are quietly eating errors, and each has a Result sibling that doesn’t.

← Previous 269. DirEntry::file_type — Stop Paying a stat() Per Entry Next → 271. fs::rename — Replace a File Atomically, Never Expose a Half-Write