Drop Bombs: Enforcing API Correctness
Use Drop to enforce invariants and detect incorrect API usage. A “drop bomb”
panics if a value is dropped without being explicitly finalized.
This pattern is often used when the finalizing operation (like commit() or
rollback()) needs to return a Result, which cannot be done from Drop.
use std::io::{self, Write}; struct Transaction { active: bool, } impl Transaction { fn start() -> Self { Self { active: true } } fn commit(mut self) -> io::Result<()> { writeln!(io::stdout(), "COMMIT")?; self.active = false; Ok(()) } } impl Drop for Transaction { fn drop(&mut self) { if self.active { panic!("Transaction dropped without commit!"); } } } fn main() -> io::Result<()> { let tx = Transaction::start(); // Use `tx` to build the transaction, then commit it. // Comment out the call to `commit` to see the panic. tx.commit()?; Ok(()) }
-
In some systems, a value must be finalized by a specific API before it is dropped.
For example, a
Transactionmight need to be committed or rolled back. -
A drop bomb ensures that a value like
Transactioncannot be silently dropped in an unfinished state. The destructor panics if the transaction has not been explicitly finalized (for example, withcommit()). -
The finalizing operation (such as
commit()) usually takesselfby value. This ensures that once the transaction is finalized, the original object can no longer be used. -
A common reason to use this pattern is when cleanup cannot be done in
Drop, either because it is fallible or asynchronous. -
This pattern is appropriate even in public APIs. It can help users catch bugs early when they forget to explicitly finalize a transactional object.
-
If cleanup can safely happen in
Drop, some APIs choose to panic only in debug builds. Whether this is appropriate depends on the guarantees your API must enforce. -
Panicking in release builds is reasonable when silent misuse would cause major correctness or security problems.
-
Question: Why do we need an
activeflag insideTransaction? Why can’tdrop()panic unconditionally?Expected answer:
commit()takesselfby value and runsdrop(), which would panic.
More to explore
Several related patterns help enforce correct teardown or prevent accidental drops.
- The
drop_bombcrate: A small utility that panics if dropped unless explicitly defused with.defuse(). Comes with aDebugDropBombvariant that only activates in debug builds.