drop() and Explicit Cleanup
std::mem::drop (included in the standard prelude as drop) is a standard library function that takes ownership of a value and destroys it immediately before its enclosing lexical scope ends.
Why std::mem::drop Exists and How It Is Implemented
Calling value.drop() directly is forbidden by the compiler (E0040) because Drop::drop(&mut self) takes a mutable reference rather than consuming self; if direct calls were permitted, the variable would remain in scope and be dropped a second time at the end of the block.
In contrast, std::mem::drop takes T by value and has an empty function body:
#[inline]
pub fn drop<T>(_x: T) {}
When you call drop(value), ownership of value moves into parameter _x, and _x goes out of scope immediately at the closing brace {} of std::mem::drop, running T's destructor and preventing any further use of value in the caller.
Caller Scope:
let guard = mutex.lock().unwrap();
*guard += 1;
drop(guard); ---> Moves `guard` into `std::mem::drop(_x: T) {}`
`_x` drops immediately inside `drop()`, releasing lock!
// Attempting to use `guard` below fails at compile time (E0382: use of moved value).
use std::sync::Mutex;
fn main() {
let counter = Mutex::new(10);
let mut guard = counter.lock().unwrap();
*guard += 5;
// Release the lock explicitly before acquiring it again on the same thread:
drop(guard);
let second_guard = counter.lock().unwrap();
println!("counter value: {}", *second_guard);
}
counter value: 15
Passing a Copy Type or Reference to drop() (dropping_copy_types Lint)
Passing a Copy type (such as i32 or &mut T) to drop(x) copies the value into _x and does not invalidate or destroy the original binding x. The compiler emits a #[warn(dropping_copy_types)] or #[warn(dropping_references)] warning to catch this mistake:
use std::cell::RefCell;
fn main() {
let cell = RefCell::new(vec![1, 2, 3]);
let borrow_guard = cell.borrow();
// WRONG: `drop(&borrow_guard)` drops a temporary shared reference `&Ref`,
// leaving `borrow_guard` alive!
// CORRECT: pass `borrow_guard` by value:
drop(borrow_guard);
// Now a mutable borrow succeeds without panicking:
cell.borrow_mut().push(4);
println!("updated cell: {:?}", cell.borrow());
}
updated cell: [1, 2, 3, 4]
Comparing std::mem::drop, std::mem::forget, and ManuallyDrop<T>
| Mechanism | Runs Destructor (Drop)? |
Consumes Value? | Primary Use Case |
|---|---|---|---|
drop(x) (std::mem::drop) |
Yes, immediately | Yes | Releasing locks, file handles, or memory buffers early |
std::mem::forget(x) |
No (leaks resources) | Yes | Transferring ownership to FFI or preventing double-drop in unsafe code |
ManuallyDrop::new(x) |
No, unless ManuallyDrop::drop is called |
Wraps x in place (#[repr(transparent)]) |
Custom smart pointer fields and union fields that need manual drop ordering |
use std::mem::ManuallyDrop;
struct LoudResource(&'static str);
impl Drop for LoudResource {
fn drop(&mut self) {
println!("LoudResource({:?}) cleaned up", self.0);
}
}
fn main() {
let early = LoudResource("early via drop()");
let mut manual = ManuallyDrop::new(LoudResource("manual via ManuallyDrop"));
let forgotten = LoudResource("forgotten via mem::forget");
drop(early);
std::mem::forget(forgotten);
println!("before manual drop");
// SAFETY: `manual` has not been dropped yet and will not be used again.
unsafe {
ManuallyDrop::drop(&mut manual);
}
println!("end of main (forgotten was never dropped)");
}
LoudResource("early via drop()") cleaned up
before manual drop
LoudResource("manual via ManuallyDrop") cleaned up
end of main (forgotten was never dropped)