File System Operations

Appending to Files

Appending to a file opens an existing (or newly created) file with the operating system's atomic append flag (O_APPEND on Unix, FILE_APPEND_DATA on Windows) via File::options().append(true).create(true).open(path) (or OpenOptions::new().append(true)...).

How append(true) Differs from write(true) + SeekFrom::End(0)

When a file is opened with .append(true):

  1. Existing contents of the file are never truncated.
  2. The operating-system kernel guarantees that every individual write system call atomically positions the file offset at the current end of the file immediately before writing, even if multiple threads or separate OS processes are appending to the same log file concurrently!
`write(true)` + Manual `seek(SeekFrom::End(0))` (Race Condition Across Processes!):
  Process A: seeks to end (offset 100) -----------------------> writes at 100 (Overwrites B!)
  Process B:                           seeks to end (100) -> writes at 100

`append(true)` (`O_APPEND` Kernel Guarantee):
  Process A: [ kernel atomically seeks to end (100) & writes 20 bytes (100..120) ]
  Process B: [ kernel atomically seeks to end (120) & writes 15 bytes (120..135) ]

Note: Because File::options().append(true) already implies .write(true), you only need .append(true).create(true).open(path) to append to a file and create it if it does not exist yet.

Appending Log Entries to a File

use std::fs::{self, File};
use std::io::{self, Write};
use std::path::Path;

fn append_log_line(path: &Path, entry: &str) -> io::Result<()> {
    let mut file = File::options()
        .append(true)
        .create(true)
        .open(path)?;

    // Write the entire line (including `\n`) in a single `.write_all()` call
    // so the OS `O_APPEND` write is a single atomic syscall:
    let formatted = format!("{entry}\n");
    file.write_all(formatted.as_bytes())?;
    Ok(())
}

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

    append_log_line(&log_path, "2026-10-26T00:00:01Z INFO server started")?;
    append_log_line(&log_path, "2026-10-26T00:00:02Z INFO listening on :8080")?;
    append_log_line(&log_path, "2026-10-26T00:00:05Z INFO health check ok")?;

    let contents = fs::read_to_string(&log_path)?;
    fs::remove_file(&log_path)?;
    print!("{}", contents);
    Ok(())
}
2026-10-26T00:00:01Z INFO server started
2026-10-26T00:00:02Z INFO listening on :8080
2026-10-26T00:00:05Z INFO health check ok