How to Create Directories Recursively in Rust

Use the `std::fs::create_dir_all` function to create a directory and any missing parent directories in a single call.

Use the std::fs::create_dir_all function to create a directory and any missing parent directories in a single call. Unlike create_dir, which fails if parent directories don't exist, create_dir_all handles the entire path hierarchy atomically and returns an error only if the operation fails due to permissions or invalid paths.

Here is a practical example creating a nested path, followed by a command-line tool snippet:

use std::fs;
use std::path::Path;

fn main() -> std::io::Result<()> {
    let path = "data/logs/2023-10-27/debug";

    // Creates 'data', 'logs', '2023-10-27', and 'debug' if they don't exist
    fs::create_dir_all(path)?;

    // Verify creation
    if Path::new(path).exists() {
        println!("Successfully created directory tree: {}", path);
    }

    Ok(())
}

For a CLI tool where you might want to handle errors gracefully without crashing immediately, you can wrap the call in a match or if let block:

use std::fs;
use std::io;

fn ensure_directory(path: &str) -> io::Result<()> {
    match fs::create_dir_all(path) {
        Ok(()) => {
            println!("Directory structure ready at: {}", path);
            Ok(())
        }
        Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => {
            // Directory already exists, which is usually fine
            println!("Directory already exists: {}", path);
            Ok(())
        }
        Err(e) => {
            eprintln!("Failed to create directory '{}': {}", path, e);
            Err(e)
        }
    }
}

Key considerations for production code:

  1. Permissions: If the parent directory exists but is read-only, create_dir_all will fail with a permission error.
  2. Idempotency: The function is idempotent; calling it on an existing directory returns Ok(()) without error.
  3. Symbolic Links: If the path contains a symbolic link that points to a non-directory, the function will fail.
  4. Atomicity: While it creates all parents, it is not strictly atomic across filesystem boundaries; if the process is killed mid-operation, you might end up with a partial directory tree.

Always use ? or explicit error handling to propagate failures, as silently ignoring errors can lead to runtime panics later when your code attempts to write files to a non-existent path.