File System Operations

File.create()

std::fs::File::create(path) opens a file in write-only mode, creating the file if it does not exist and immediately truncating its length to 0 bytes if it already exists. It is a shorthand for OpenOptions::new().write(true).create(true).truncate(true).open(path).

Comparing File::create, File::create_new, and OpenOptions::append

Because File::create(path) silently wipes out any existing file at path, Rust also provides File::create_new(path) (stabilized in Rust 1.77), which atomically creates a new file and fails with ErrorKind::AlreadyExists if a file or symlink already exists at path:

Method Mode If File Does NOT Exist If File ALREADY Exists
File::create(path) Write-only Creates new file Truncates existing file to 0 bytes!
File::create_new(path) Read + Write Atomically creates new file Fails with ErrorKind::AlreadyExists (never overwrites!)
File::options().append(true).open(path) Append Fails with NotFound (unless .create(true) added) Preserves existing bytes and appends at end
Danger of `File::create` vs. Safety of `File::create_new`:

  `File::create("important.db")`     ---> Overwrites and zeroes out existing `important.db`!
  `File::create_new("important.db")` ---> Uses OS `O_CREAT | O_EXCL`: atomic check-and-create,
                                          returns `Err(AlreadyExists)` if file is present!
use std::fs::{self, File};
use std::io::{self, ErrorKind, Write};

fn main() -> io::Result<()> {
    let path = std::env::temp_dir().join("rust_ch50_create_demo.txt");
    let _ = fs::remove_file(&path);

    // 1. `File::create_new` succeeds when the file does not exist yet:
    {
        let mut f = File::create_new(&path)?;
        f.write_all(b"initial important data\n")?;
    }

    // 2. A second `File::create_new` on the same path fails with `AlreadyExists`
    //    without harming the existing file!
    match File::create_new(&path) {
        Ok(_) => unreachable!("file already exists"),
        Err(e) if e.kind() == ErrorKind::AlreadyExists => {
            println!("create_new prevented accidental overwrite (AlreadyExists)");
        }
        Err(e) => return Err(e),
    }

    // 3. `File::create` truncates and replaces the existing file:
    {
        let mut f = File::create(&path)?;
        f.write_all(b"replaced\n")?;
    }

    let final_text = fs::read_to_string(&path)?;
    fs::remove_file(&path)?;
    print!("final content after File::create: {}", final_text);
    Ok(())
}
create_new prevented accidental overwrite (AlreadyExists)
final content after File::create: replaced

Parent Directory Must Exist

Neither File::create nor File::create_new automatically creates missing parent directories. If "output/reports/q1.csv" is passed to File::create and "output/reports" does not exist yet, File::create returns Err(ErrorKind::NotFound). Call if let Some(parent) = path.parent() { fs::create_dir_all(parent)?; } first when writing to a nested directory path.