Defer in Rust

In Rust, we use the Drop trait to ensure that a function is called when a value goes out of scope, which is similar to Go’s defer. This is often used for cleanup purposes.

use std::fs::File;
use std::io::Write;

fn main() {
    // Immediately after getting a file object with create_file,
    // we rely on Rust's RAII to automatically close the file
    // when it goes out of scope at the end of main().
    let f = create_file("/tmp/defer.txt");
    write_file(&f);
    // The file will be automatically closed here.
}

fn create_file(p: &str) -> File {
    println!("creating");
    match File::create(p) {
        Ok(file) => file,
        Err(e) => panic!("Failed to create file: {}", e),
    }
}

fn write_file(f: &File) {
    println!("writing");
    if let Err(e) = writeln!(f, "data") {
        eprintln!("Couldn't write to file: {}", e);
    }
}

// In Rust, we don't need a separate close_file function.
// The File type implements the Drop trait, which automatically
// closes the file when it goes out of scope.

In this Rust version:

  1. We use File::create to create a file, which returns a Result. We use pattern matching to handle the result.

  2. Instead of explicitly deferring the close operation, we rely on Rust’s RAFI (Resource Acquisition Is Initialization) principle. The File type implements the Drop trait, which automatically closes the file when it goes out of scope.

  3. We use the write! macro (specifically writeln! for writing with a newline) to write to the file.

  4. Error handling is done using Result and the ? operator or explicit matching.

Running the program confirms that the file is created, written to, and then automatically closed when it goes out of scope:

$ cargo run
creating
writing

In Rust, resource cleanup is typically handled automatically through the Drop trait, which provides better safety guarantees compared to manual resource management. This approach helps prevent resource leaks and makes the code safer and more concise.