Drop Bombs: using std::mem::forget
use std::io::{self, Write}; struct Transaction; impl Transaction { fn start() -> Self { Transaction } fn commit(self) -> io::Result<()> { writeln!(io::stdout(), "COMMIT")?; // Defuse the drop bomb by preventing Drop from ever running. std::mem::forget(self); Ok(()) } } impl Drop for Transaction { fn drop(&mut self) { // This is the "drop bomb" 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(()) }
This example removes the flag from the previous slide and makes the drop method
panic unconditionally. To avoid that panic on a successful commit, the commit
method now takes ownership of the transaction and calls
std::mem::forget, which
prevents the Drop::drop() method from running.
If the forgotten value owned heap allocated memory that would normally be freed
in its drop() implementation, one consequence is a memory leak. That is not
the case for the Transaction in the example above, since it does not own any
heap memory.
We can avoid needing a runtime flag by using mem::forget() in a tactical way.
When the transaction commits successfully, we can defuse the drop bomb by
calling std::mem::forget on the value, which prevents its Drop
implementation from running.