21.4Drop in depth
Since lesson 8.3 you've trusted that values clean themselves up. A String frees its heap buffer at the closing brace; a File closes itself; a Box deallocates. We called this "dropping" and moved on. Now you can see the mechanism: the Drop trait. A type that implements Drop gets to run code at the exact moment a value of that type goes out of scope, and that hook is how every self-cleaning type in Rust does its cleaning.
Implementing Drop
Drop has one method, drop, which the compiler calls automatically when the value is about to be destroyed. You never call it yourself; you just define what should happen. Here's a type that announces its own demise:
struct Guard {
name: String,
}
impl Drop for Guard {
fn drop(&mut self) {
println!("dropping Guard '{}'", self.name);
}
}
fn main() {
let _a = Guard { name: String::from("a") };
let _b = Guard { name: String::from("b") };
println!("guards created");
}guards created
dropping Guard 'b'
dropping Guard 'a'
The two println!s inside drop fire after "guards created", at the end of main, with no call from you. When a Guard goes out of scope, the compiler runs its drop. This is precisely what String and Box do; their drop implementations free heap memory instead of printing. Any cleanup a type needs, closing a connection, releasing a lock, flushing a buffer, goes in drop, and then it happens automatically and reliably.
Drop order
Look again at that output: b dropped before a, even though a was created first. Values in a scope drop in reverse order of declaration, last created, first dropped. This is deliberate and it matters: a later value might depend on an earlier one (it was, after all, created while the earlier one existed), so unwinding in reverse means nothing is torn down while something that might use it is still alive. It's the same last-in-first-out discipline as the stack itself (lesson 8.2).
Fields within a struct drop in declaration order (top to bottom), and a struct is dropped before the variables declared before it. You rarely need to think about the exact order, but when two resources must be released in a particular sequence, knowing that "reverse of creation" is the rule lets you arrange declarations to get it.
Dropping early with std::mem::drop
Sometimes you want a value gone before its scope ends, releasing a lock early, say, so other code can proceed. You can't call the drop method yourself; Rust forbids it, because the value would then still be dropped automatically at scope end, dropping it twice (the double-free chapter 8 exists to prevent). Instead, the standard library gives you a free function, std::mem::drop, that takes ownership of a value and drops it then and there:
struct Guard {
name: String,
}
impl Drop for Guard {
fn drop(&mut self) {
println!("dropping '{}'", self.name);
}
}
fn main() {
let g = Guard { name: String::from("early") };
println!("before drop");
drop(g); // std::mem::drop, in the prelude
println!("after drop, g is gone");
}before drop
dropping 'early'
after drop, g is gone
drop(g) takes ownership of g and ends its life immediately; the drop runs between the two prints, not at the end of main. Because drop(g) moved g (chapter 8), g is no longer usable afterward, so there's no risk of a second cleanup. This drop function is in the prelude, so you call it without importing. It's the safe, ownership-respecting way to say "I'm done with this now."
You can't call .drop() yourself
g.drop() is a compile error (E0040: "explicit use of destructor method"). The reason is double-free safety: if you could run the destructor by hand, the value would still be dropped automatically at the end of its scope, cleaning up twice. Rust closes that door entirely, the destructor method is callable only by the compiler, and routes early cleanup through std::mem::drop(g), which moves the value out so the automatic drop can't also fire. The error message even points you to the drop function.
RAII: the pattern, named
This whole arrangement, a resource acquired when a value is created and released when the value is dropped, has a name from C++: RAII, "Resource Acquisition Is Initialization." The ungainly phrase means something simple: tie a resource's lifetime to a value's lifetime, so that going out of scope is releasing the resource. Open a file by creating a File; the file closes when the File drops. Lock a mutex by creating a guard; it unlocks when the guard drops (you'll see exactly this in chapter 22). Allocate by creating a Box; it frees on drop.
For readers coming from C++, this is the familiar destructor-driven resource management, and Rust's version is the same idea with sharper teeth: ownership and the borrow checker guarantee drop runs exactly once, at a well-defined time, with no way to forget it or do it twice. For readers coming from garbage-collected languages, RAII is the answer to "but who closes the file?", the answer is "scope does, deterministically," not "the collector, eventually." It's why Rust needs neither manual free/close nor a garbage collector: Drop plus ownership cleans up everything, precisely when the owner goes away.
Key insight
Drop plus ownership is Rust's entire resource-management story. Every resource, heap memory, files, locks, network connections, is owned by some value, and releasing it is that value's drop, run automatically when ownership ends. No garbage collector (cleanup is deterministic, at scope exit) and no manual frees (you can't forget, and you can't double-free). The automatic cleanup you've trusted since chapter 8 was Drop the whole time; now you can write your own.
Quiz time
Question #1
When does a type's drop method run, and who calls it?
Show solution
The compiler calls drop automatically when a value goes out of scope (or is otherwise destroyed, e.g. via std::mem::drop). You never call the drop method yourself, you only define it via impl Drop. It's the hook for cleanup (freeing memory, closing files, releasing locks), and it runs deterministically at the end of the value's life.
Question #2
In what order are values in a scope dropped, and why that order?
Show solution
Reverse order of declaration, last created, first dropped (LIFO, like the stack). The reason: a later value may have been built using or depending on an earlier one, so tearing down in reverse ensures nothing is destroyed while something that might still reference it is alive. (Struct fields drop in declaration order, top to bottom.)
Question #3
Why must you use std::mem::drop(value) to drop something early, rather than calling value.drop()?
Show solution
Calling the drop method directly is a compile error, because the value would still be dropped automatically at scope end, causing a double free. std::mem::drop(value) instead takes ownership of the value (moving it), runs the destructor immediately, and, because the value has been moved out, the automatic end-of-scope drop no longer applies. It's the ownership-safe way to end a value's life early (e.g. to release a lock before scope ends).
Box gives single ownership, and Drop cleans it up. But some data genuinely needs more than one owner. The next lesson (21.5) introduces Rc<T>, the reference-counted smart pointer that lets a value be owned jointly and freed only when the last owner lets go.