Drop and Resource Management

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)