#269 Jul 19, 2026

269. DirEntry::file_type — Stop Paying a stat() Per Entry

Filtering read_dir entries with path.is_file() costs a full metadata lookup per entry — and quietly reports false when the check fails. DirEntry::file_type() is free on most platforms and tells you when it couldn’t answer.

This morning’s bite 268 collected read_dir entries into paths. The next thing most code does is filter them, and the obvious way has two hidden problems:

1
2
3
4
5
6
for entry in fs::read_dir("logs")? {
    let path = entry?.path();
    if path.is_file() {          // stat() per entry
        process(&path);
    }
}

First, Path::is_file() asks the filesystem for full metadata — an extra syscall for every entry, on top of the directory read that already happened. Second, it returns plain bool: if the metadata lookup fails (permissions, a file deleted mid-loop), you get false, indistinguishable from “this is a directory”. Errors vanish.

The entry you already have knows its own type:

1
2
3
4
5
6
for entry in fs::read_dir("logs")? {
    let entry = entry?;
    if entry.file_type()?.is_file() {
        process(&entry.path());
    }
}

Two things improved:

  • no extra syscall — on Linux, readdir returns each entry’s type alongside its name (d_type), and Windows directory entries carry file attributes. file_type() just hands you what the directory read already produced. (A few filesystems don’t fill d_type in; std falls back to a metadata call only then.)
  • errors surfacefile_type() returns io::Result<FileType>, so a failed lookup is a ?-able error instead of a silent false

One behavioral difference to know: file_type() does not follow symlinks — a symlink to a file reports is_symlink(), not is_file(), while Path::is_file() traverses the link and reports on the target. If following symlinks is what you actually want, that’s the one case for fs::metadata(entry.path()) — deliberately, not by accident.

← Previous 268. fs::read_dir — The Filesystem Doesn't Sort for You Next → 270. Path::try_exists — exists() Can't Tell "Missing" From "Couldn't Check"