Drop and Resource Management

The Drop Trait

std::ops::Drop is the standard library trait that hooks custom cleanup code into a type's destructor. It is included in the standard prelude, so Drop can be implemented without an explicit use import.

Trait Definition

pub trait Drop {
    fn drop(&mut self);
}

The drop method receives an exclusive mutable reference &mut self to the instance being destroyed. Once Drop::drop(&mut self) finishes executing, the compiler automatically drops each field of self in source declaration order.

Custom Drop::drop vs. Compiler-Generated Drop Glue

A type does not need to implement Drop explicitly to clean up its fields. The compiler synthesizes drop glue (std::ptr::drop_in_place<T>) for every composite type that transitively contains a resource-owning type:

Destroy `Session` instance:
  |
  +-- 1. Does `Session` implement `Drop`?
  |        Yes -> Call `<Session as Drop>::drop(&mut self)`
  |        No  -> Skip custom hook
  |
  +-- 2. Run compiler-generated drop glue on each field in declaration order:
           +-- Drop `self.name: String`      (deallocates UTF-8 heap buffer)
           +-- Drop `self.buffer: Vec<u8>`   (deallocates Vec heap buffer)
           +-- `self.port: u16`              (trivial, no-op)
struct InnerBuffer {
    label: &'static str,
}

impl Drop for InnerBuffer {
    fn drop(&mut self) {
        println!("InnerBuffer ({}) dropped by compiler drop glue", self.label);
    }
}

// `OuterPacket` does NOT implement `Drop` explicitly,
// yet its `InnerBuffer` field is still dropped automatically:
struct OuterPacket {
    id: u32,
    inner: InnerBuffer,
}

fn main() {
    let packet = OuterPacket {
        id: 7,
        inner: InnerBuffer { label: "payload-7" },
    };
    println!("created packet {}", packet.id);
}
created packet 7
InnerBuffer (payload-7) dropped by compiler drop glue

Mutual Exclusion of Drop and Copy (E0184)

A type cannot implement both Drop and Copy. Copy types are duplicated implicitly via bitwise copies on assignment, argument passing, and return; if a Copy type also had a destructor, every implicit bitwise copy would run the same destructor on the same underlying resource handle, causing double frees. Attempting to implement both traits produces compiler error E0184:

// ❌ does not compile
#[derive(Clone, Copy)]
struct FileDescriptor(i32);

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

fn main() {}
error[E0184]: the trait `Copy` cannot be implemented for this type; the type has a destructor
 --> src/main.rs:2:17
  |
2 | #[derive(Clone, Copy)]
  |                 ^^^^ `Copy` not allowed on types with destructors

Checking Whether a Type Needs Drop (std::mem::needs_drop)

The const function std::mem::needs_drop::<T>() -> bool returns true if T or any of its transitive fields has non-trivial drop glue, and false for trivially destructible types (primitives, raw pointers, references, and structs/arrays composed solely of trivial types). Custom collection implementations use needs_drop::<T>() to skip element-by-element destructor loops when clearing buffers of trivial types.

use std::mem::needs_drop;

struct TrivialPair {
    a: u64,
    b: bool,
}

struct OwningPair {
    a: u64,
    text: String,
}

fn main() {
    println!("needs_drop::<u64>():         {}", needs_drop::<u64>());
    println!("needs_drop::<&String>():     {}", needs_drop::<&String>());
    println!("needs_drop::<TrivialPair>(): {}", needs_drop::<TrivialPair>());
    println!("needs_drop::<OwningPair>():  {}", needs_drop::<OwningPair>());
}
needs_drop::<u64>():         false
needs_drop::<&String>():     false
needs_drop::<TrivialPair>(): false
needs_drop::<OwningPair>():  true