drop.md (2922B)
1 # Drop 2 3 Rust’s Drop trait lets a type run custom cleanup code when a value is about to 4 go out of scope. 5 6 The drop method is called automatically when an object goes out of scope. It is 7 part of the std library in Rust. Box, Vec, String, File, and Process are some 8 examples of types that implement the Drop trait to free resources. 9 10 ## Examples 11 12 There are three way to manage drop: 13 14 1. **Inner block (idiomatic, no extra types)** 15 16 Put everything that holds the DB in a nested scope. When the block ends, service 17 and db are dropped automatically, then you delete the file. 18 19 ```rust 20 let path = create_test_db_path(); 21 { 22 let db = Arc::new(open_test_db(&path)); let 23 service = setup_test_service(db.clone()).await.unwrap(); 24 // … test body … 25 } 26 cleanup_test_db(path); 27 ``` 28 29 No manual drop—the closing } defines when the handles go away. 30 31 2. **RAII “temp file” guard** 32 33 Use a small type that implements Drop and calls remove_file in drop, and declare 34 it before the `Arc<DB>` so the DB is dropped first (last declared → dropped 35 first): 36 37 ```rust 38 let path = create_test_db_path(); 39 let _tmp = TempRedb::new(path.clone()); // deletes path on Drop 40 let db = Arc::new(open_test_db(&path)); 41 // … when function/block ends: db drops, then _tmp deletes the file 42 ``` 43 44 Same idea as tempfile::TempPath / NamedTempFile in the tempfile crate: the guard 45 runs cleanup when it goes out of scope, after earlier locals are dropped. 46 47 3. **Manual cleanup before the end of scope** 48 49 ```rust 50 use std::fs; 51 use std::path::Path; 52 53 struct TestDb { 54 path: String, 55 } 56 57 impl TestDb { 58 fn new(path: &str) -> Self { 59 println!("Opening DB at {}", path); 60 Self { path: path.to_string() } 61 } 62 } 63 64 impl Drop for TestDb { 65 fn drop(&mut self) { 66 println!("Dropping TestDb for {}", self.path); 67 // In real code: close DB connection, etc. 68 } 69 } 70 71 fn cleanup_db(path: &str) { 72 if Path::new(path).exists() { 73 fs::remove_file(path).unwrap(); 74 println!("Cleaned up DB file: {}", path); 75 } 76 } 77 78 fn main() { 79 let path = "test.db"; 80 81 let db = TestDb::new(path); // declared first → dropped last 82 let _guard = std::fs::File::open(path).unwrap_or_else(|_| { 83 // create dummy file for demo 84 fs::write(path, "").unwrap(); 85 fs::File::open(path).unwrap() 86 }); 87 88 // ... test body ... 89 90 // Explicitly drop `db` *before* `_guard`, even though `db` was declared first 91 drop(db); // ← explicit drop call 92 93 // Now safe to clean up file (since db is gone) 94 cleanup_db(path); 95 96 // `_guard` is dropped here automatically at end of scope 97 } 98 ``` 99 100 ──────────────────────────────────────── 101 102 Summary: Prefer a block for tests; use a guard (or tempfile) if you want cleanup 103 bundled in one place. You only need explicit drop when you want a non-default 104 order without restructuring scopes.