Rust’s Drop trait lets a type run custom cleanup code when a value is about to go out of scope.

The drop method is called automatically when an object goes out of scope. It is part of the std library in Rust. Box, Vec, String, File, and Process are some examples of types that implement the Drop trait to free resources.

Examples

There are three way to manage drop:

  1. Inner block (idiomatic, no extra types)

Put everything that holds the DB in a nested scope. When the block ends, service and db are dropped automatically, then you delete the file.

let path = create_test_db_path();
{
    let db = Arc::new(open_test_db(&path)); let
    service = setup_test_service(db.clone()).await.unwrap();
    // … test body …
}
cleanup_test_db(path);

No manual drop—the closing } defines when the handles go away.

  1. RAII “temp file” guard

Use a small type that implements Drop and calls remove_file in drop, and declare it before the Arc<DB> so the DB is dropped first (last declared → dropped first):

let path = create_test_db_path();
let _tmp = TempRedb::new(path.clone()); // deletes path on Drop
let db = Arc::new(open_test_db(&path));
// … when function/block ends: db drops, then _tmp deletes the file

Same idea as tempfile::TempPath / NamedTempFile in the tempfile crate: the guard runs cleanup when it goes out of scope, after earlier locals are dropped.

  1. Manual cleanup before the end of scope
use std::fs;
use std::path::Path;
 
struct TestDb {
    path: String,
}
 
impl TestDb {
    fn new(path: &str) -> Self {
        println!("Opening DB at {}", path);
        Self { path: path.to_string() }
    }
}
 
impl Drop for TestDb {
    fn drop(&mut self) {
        println!("Dropping TestDb for {}", self.path);
        // In real code: close DB connection, etc.
    }
}
 
fn cleanup_db(path: &str) {
    if Path::new(path).exists() {
        fs::remove_file(path).unwrap();
        println!("Cleaned up DB file: {}", path);
    }
}
 
fn main() {
    let path = "test.db";
 
    let db = TestDb::new(path); // declared first → dropped last
    let _guard = std::fs::File::open(path).unwrap_or_else(|_| {
        // create dummy file for demo
        fs::write(path, "").unwrap();
        fs::File::open(path).unwrap()
    });
 
    // ... test body ...
 
    // Explicitly drop `db` *before* `_guard`, even though `db` was declared first
    drop(db); // ← explicit drop call
 
    // Now safe to clean up file (since db is gone)
    cleanup_db(path);
 
    // `_guard` is dropped here automatically at end of scope
}

────────────────────────────────────────

Summary: Prefer a block for tests; use a guard (or tempfile) if you want cleanup bundled in one place. You only need explicit drop when you want a non-default order without restructuring scopes.