For now, this reference is a best-effort document. We strive for validity and completeness, but are not yet there. In the future, the docs and lang teams will work together to figure out how best to do this. Until then, this is a best-effort attempt. If you find something wrong or missing, file an issue or send in a pull request.

Destructors

When an initialized variable in Rust goes out of scope or a temporary is no longer needed its destructor is run. Assignment also runs the destructor of its left-hand operand, unless it's an uninitialized variable. If a struct variable has been partially initialized, only its initialized fields are dropped.

The destructor of a type consists of

  1. Calling its std::ops::Drop::drop method, if it has one.
  2. Recursively running the destructor of all of its fields.
    • The fields of a struct, tuple or enum variant are dropped in declaration order. *
    • The elements of an array or owned slice are dropped from the first element to the last. *
    • The captured values of a closure are dropped in an unspecified order.
    • Trait objects run the destructor of the underlying type.
    • Other types don't result in any further drops.

* This order was stabilized in RFC 1857.

Variables are dropped in reverse order of declaration. Variables declared in the same pattern drop in an unspecified ordered.

If a destructor must be run manually, such as when implementing your own smart pointer, std::ptr::drop_in_place can be used.

Some examples:


# #![allow(unused_variables)]
#fn main() {
struct ShowOnDrop(&'static str);

impl Drop for ShowOnDrop {
    fn drop(&mut self) {
        println!("{}", self.0);
    }
}

{
    let mut overwritten = ShowOnDrop("Drops when overwritten");
    overwritten = ShowOnDrop("drops when scope ends");
}
# println!("");
{
    let declared_first = ShowOnDrop("Dropped last");
    let declared_last = ShowOnDrop("Dropped first");
}
# println!("");
{
    // Tuple elements drop in forwards order
    let tuple = (ShowOnDrop("Tuple first"), ShowOnDrop("Tuple second"));
}
# println!("");
loop {
    // Tuple expression doesn't finish evaluating so temporaries drop in reverse order:
    let partial_tuple = (ShowOnDrop("Temp first"), ShowOnDrop("Temp second"), break);
}
# println!("");
{
    let moved;
    // No destructor run on assignment.
    moved = ShowOnDrop("Drops when moved");
    // drops now, but is then uninitialized
    moved;

    // Uninitialized does not drop.
    let uninitialized: ShowOnDrop;

    // After a partial move, only the remaining fields are dropped.
    let mut partial_move = (ShowOnDrop("first"), ShowOnDrop("forgotten"));
    // Perform a partial move, leaving only `partial_move.0` initialized.
    core::mem::forget(partial_move.1);
    // When partial_move's scope ends, only the first field is dropped.
}
#}

Not running destructors

Not running destructors in Rust is safe even if it has a type that isn't 'static. std::mem::ManuallyDrop provides a wrapper to prevent a variable or field from being dropped automatically.