notes

Log | Files | Refs | README

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.