Recipe
Replace a file atomically
Use when: a program rewrites a file that must never be seen half-written: settings, a summary, a saved game, a database's index
Goal: the file holds either its complete old contents or its complete new contents, whatever happens during the save
Taught in Errors and edge cases
Quick recipe
-
1
Build the temporary path next to the target, for example
path.with_extension("tmp").Example
For
summary.txt,path.with_extension("tmp")givessummary.tmp, in the same folder. Fordata/summary.txtit givesdata/summary.tmp.Why: rename only replaces in one step within one filesystem, and a folder is always on one filesystem.
-
2
let mut f = File::create(&tmp)?;thenf.write_all(bytes)?(orwriteln!in a loop).Example
The new summary has three lines:
min 8.1,max 17.9andmean 12.0. The program writes them tosummary.tmpwithwriteln!. Say the power cuts aftermin 8.1. Thensummary.tmpholds one line, andsummary.txtstill holds all three old lines, just asatomic.txtdid in the program above.Why: all the risky work happens to a file nobody else is reading, so a crash here costs nothing.
-
3
f.sync_all()?;then letfdrop, closing the file.Example
In the program above,
write_linesends withfile.sync_all(). It runs only after every line was written, and the rename comes after it. So the rename never points the name at data that is still only in memory.Why: a completed write can still be in the operating system's memory; syncing makes sure the rename can't expose an empty file after a power cut.
-
4
fs::rename(&tmp, &path)?;Example
fs::rename(&tmp, &path)?renamessummary.tmptosummary.txt. A reader who openssummary.txtjust before gets the three old lines. One who opens it just after gets the three new lines. The namesummary.tmpno longer exists.Why: the rename swaps which contents the name points to in one step, so readers see the old file or the new one, never a mix.
Common mistakes
- Putting the temporary file in a system temp folder on another disk: the rename then fails with
CrossesDevices. - Skipping
sync_all: after a power cut the new name can point at a file whose data never reached the disk. - Using one fixed temporary name when two programs may save at once: they overwrite each other's temporary file. Use a unique name (section 6).