Shiklo Tools
PDF40
Image20
Calculators & Converters25
Text & Writing13
Developer26
Video14
Audio14
Document14
Data & Spreadsheet12
Web & SEO9
OCR & Scanning5
Archive & File12
Security & Privacy23
Health & Medical29
QR & Barcode5

Rust Files

Variables disappear when a program ends. To keep information for next time, such as a high score, a to-do list or a diary, a program saves it in a file. Rust's file tools live in std::fs, short for file system. In our editor, files your program creates appear in the file list on the left, so you can open them and see what was written.

Writing and reading a whole file

The simplest tools are fs::write, which saves a piece of text into a file (creating it, or replacing what was there), and fs::read_to_string, which reads a whole file into a String:

use std::fs;

fn main() {
    fs::write("hello.txt", "Hello from Rust!\nThis is line two.\n").expect("could not write");
    let text = fs::read_to_string("hello.txt").expect("could not read");
    print!("{}", text);
    println!("The file has {} lines.", text.lines().count());
}
Output
Hello from Rust!
This is line two.
The file has 2 lines.

Both return a Result, because working with files can fail: the disk might be full, or the file might not exist. Here expect stops the program with a message if that happens.

Going through a file line by line

lines() splits text into its lines, which is the most common way to process a file. Here we save a few marks and then work out the average from the file:

use std::fs;

fn main() {
    fs::write("marks.txt", "Asha 91\nRavi 78\nLeela 85\n").expect("could not write");

    let text = fs::read_to_string("marks.txt").expect("could not read");
    let mut total = 0;
    let mut count = 0;
    for line in text.lines() {
        let parts: Vec<&str> = line.split_whitespace().collect();
        let mark: i32 = parts[1].parse().expect("a number");
        println!("{} scored {}", parts[0], mark);
        total += mark;
        count += 1;
    }
    println!("Average: {:.1}", total as f64 / count as f64);
}
Output
Asha scored 91
Ravi scored 78
Leela scored 85
Average: 84.7

Adding to a file

fs::write replaces the whole file. To add lines at the end instead, open the file with OpenOptions in append mode and write with writeln!, which works like println! but into the file:

use std::fs::{self, OpenOptions};
use std::io::Write;

fn main() {
    fs::write("diary.txt", "Monday: started learning Rust\n").expect("could not write");
    let mut file = OpenOptions::new().append(true).open("diary.txt").expect("could not open");
    writeln!(file, "Tuesday: learnt about ownership").expect("could not write");
    writeln!(file, "Wednesday: wrote my first struct").expect("could not write");

    print!("{}", fs::read_to_string("diary.txt").expect("could not read"));
}
Output
Monday: started learning Rust
Tuesday: learnt about ownership
Wednesday: wrote my first struct

use std::io::Write; brings in the Write trait, which gives files their write abilities. If you forget it, the compiler suggests adding it.

When the file is missing

Instead of stopping the program, a match can handle a missing file gracefully, for example by starting fresh:

use std::fs;

fn main() {
    match fs::read_to_string("settings.txt") {
        Ok(text) => println!("Settings: {}", text),
        Err(e) => println!("No settings yet ({}), using the defaults.", e),
    }
}
Output
No settings yet (No such file or directory (os error 44)), using the defaults.

The exact wording of the error comes from the system the program runs on; on your own computer it might look slightly different.

Practice exercises

  1. Write a program that saves your three favourite films to films.txt, one per line, and then prints them numbered.
  2. Make a counter that reads a number from count.txt, adds one, and writes it back (start at 0 if the file is missing).
  3. Append the current step of a recipe to recipe.txt each time you run the program.