🎓 A1Learner
🔍

Rust Writing Files

Writing a file saves data from a program into a file. Every write can fail, so every write returns a Result.

Save a whole file in one call

fs::write creates the file — or replaces it completely — and saves the whole text at once. Like every call in std::fs, it hands back an io::Result:

use std::fs;
fs::write("notes.txt", "first line\n").expect("write failed");
let text = fs::read_to_string("notes.txt").expect("read failed");
print!("{text}");

Hold the file open

File::create also replaces an existing file, then stays open. write_all writes raw bytes in — text goes in as text.as_bytes() — and writeln! writes one formatted line. The file closes itself when the handle goes out of scope:

use std::fs::{self, File};
use std::io::Write;
let mut file = File::create("log.txt").expect("create failed");
file.write_all("hello".as_bytes()).expect("write failed");
writeln!(file, " again").expect("write failed");
drop(file);
print!("{}", fs::read_to_string("log.txt").expect("read failed"));

Add to the end with append

OpenOptions chooses the behaviour first: .append(true) adds to the end instead of replacing, and .create(true) makes the file only when it is missing:

use std::fs::{self, OpenOptions};
use std::io::Write;
fs::write("list.txt", "one\n").expect("write failed");
let mut file = OpenOptions::new().append(true).create(true).open("list.txt").expect("open failed");
writeln!(file, "two").expect("append failed");
drop(file);
print!("{}", fs::read_to_string("list.txt").expect("read failed"));

Folders and deleting

create_dir_all builds a folder together with any missing parent folders, and remove_file deletes one file:

use std::fs;
fs::create_dir_all("data/logs").expect("mkdir failed");
fs::write("data/logs/a.txt", "x").expect("write failed");
println!("exists = {}", std::path::Path::new("data/logs/a.txt").exists());
fs::remove_file("data/logs/a.txt").expect("remove failed");
println!("after remove = {}", std::path::Path::new("data/logs/a.txt").exists());

Many small writes? Buffer them

BufWriter keeps the pieces in memory. It sends them to disk in bigger chunks. This is much faster than one system write per line. Call flush() (or let the writer drop) so the last chunk really lands:

use std::fs::{self, File};
use std::io::{BufWriter, Write};
let file = File::create("many.txt").expect("create failed");
let mut writer = BufWriter::new(file);
for n in 1..=3 {
    writeln!(writer, "line {n}").expect("write failed");
}
writer.flush().expect("flush failed");
drop(writer);
print!("{}", fs::read_to_string("many.txt").expect("read failed"));

Example

use std::fs;
use std::io::Write;
fn main() {
    fs::write("notes.txt", "first line\n").expect("could not write the file");
    {
        let mut file = fs::OpenOptions::new().append(true).open("notes.txt").expect("could not open the file");
        writeln!(file, "second line").expect("could not append");
    }
    let text = fs::read_to_string("notes.txt").expect("could not read the file back");
    print!("{text}");
    println!("bytes = {}", text.len());
}

Key points

  • Writing a file saves program data into a file, and each write reports failure as a Result.
  • The tempfile crate is the usual helper.
  • Its NamedTempFile writes to a scratch file that deletes itself when dropped.
  • The file can then be renamed over the real file in one step

In this example, the example writes its own file first, so it can run anywhere. fs::write saves first line plus its newline in one call. The block then opens the same file with OpenOptions in append mode. writeln! adds second line at the end. Leaving the block closes the file. read_to_string reads the finished file back: first line and second line, 11 + 12 = 23 bytes in total. Without append, the second open would have replaced the file and only second line would remain.

Note

Replacing is the default for both fs::write and File::create — if the file already existed, its old contents are gone.

To keep them, open the file with append.

Some programs must never leave half-written files behind.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

You open an existing file with File::create and write a short note. What happens to its old contents?