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