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:
- 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.
- 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 fileSame idea as tempfile::TempPath / NamedTempFile in the tempfile crate: the guard runs cleanup when it goes out of scope, after earlier locals are dropped.
- 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.