#272 Jul 21, 2026

272. fs::create_dir_all — mkdir -p Without the exists() Check

fs::create_dir fails if the parent is missing and fails if the directory already exists. create_dir_all handles both — recursive and idempotent in one call.

The single-level version trips over both ends of the problem:

1
2
3
4
5
// Error: NotFound — `cache/images` doesn't exist yet.
fs::create_dir("cache/images/thumbs")?;

// Error: AlreadyExists — on the second run.
fs::create_dir("cache")?;

So people reach for the guard:

1
2
3
if !dir.exists() {
    fs::create_dir(dir)?; // still one level at a time
}

That’s a check-then-act race (bite 270 again): another process can create the directory between the check and the call, and you still can’t build a nested path in one go.

create_dir_all is the whole fix:

1
2
fs::create_dir_all("cache/images/thumbs")?; // creates all three
fs::create_dir_all("cache/images/thumbs")?; // Ok — already there

It creates every missing component along the path, and it returns Ok when the directory already exists — no exists() probe, no AlreadyExists special-casing, safe to call on every startup.

Three details worth knowing:

  • It’s concurrency-tolerant. If another thread or process creates one of the components mid-walk, create_dir_all shrugs and keeps going. The exists() guard can’t promise that.
  • A file in the way is still an error. If cache/images exists but is a regular file, you get an error instead of silent breakage — exactly what you want.
  • It won’t touch what’s already there. Permissions of existing directories are left alone; only newly created components get the default (or DirBuilder-configured) mode.

Same moral as the rest of this filesystem run: don’t interrogate the filesystem and then act on the answer — call the operation that already handles every case.

← Previous 271. fs::rename — Replace a File Atomically, Never Expose a Half-Write Next → 273. remove_file — Deleting Something That Might Already Be Gone