Skip to content

Q031. What do mem::forget and ManuallyDrop do to ownership and destruction?

Short Answer

mem::forget consumes a value and prevents its destructor from running, usually leaking its resources. ManuallyDrop<T> wraps a value and disables automatic destruction while preserving T's layout and validity requirements. The value can later be recovered or manually destroyed, but unsafe manual destruction must prevent double drop, use-after-drop, and invalid moves.

Deep Dive

mem::forget(value) is safe because Rust never guarantees that destructors must run. After the call, the value is inaccessible and its cleanup responsibility has been intentionally abandoned. It is rarely the best ownership-transfer primitive because failures before the call can still run the old destructor.

ManuallyDrop::new(value) disables automatic dropping immediately. Safe code can access the contained value and can recover it with ManuallyDrop::into_inner, restoring normal destruction. Low-level code may use unsafe ManuallyDrop::take or ManuallyDrop::drop when manually managing an active variant, union field, or raw ownership transfer.

ManuallyDrop does not provide uninitialized storage; MaybeUninit<T> serves that purpose. Public generic wrappers around manually dropped values are especially difficult to make sound.

Internal Model

ManuallyDrop<T> has the same layout and bit-validity requirements as T; it adds no runtime flag. The compiler simply omits automatic drop glue for the wrapped field.

After unsafe manual destruction, the bytes may remain, but they must not be exposed as a live T. Some cases, such as moving a ManuallyDrop<Box<_>> after manually dropping its contents, have additional validity hazards.

Example

rust
use std::mem::{self, ManuallyDrop};

fn main() {
    let forgotten = String::from("leaked");
    mem::forget(forgotten);

    let manual = ManuallyDrop::new(String::from("recoverable"));
    println!("{}", manual.len());

    let recovered = ManuallyDrop::into_inner(manual);
    drop(recovered);
}

Common Mistakes

  • Using ManuallyDrop<T> as if it were uninitialized storage.
  • Calling ManuallyDrop::drop twice or exposing the value afterward.
  • Using mem::forget as the default method for transferring raw ownership.

Follow-ups

  • Why is ManuallyDrop::drop unsafe?
  • When should MaybeUninit<T> be used instead?
  • How can panic between raw ownership steps cause double cleanup?

References

  • Rust Reference
  • Rust Standard Library
  • Rustonomicon