Step 7 of 7
Drop and Deref
Drop: code that runs when a value goes away
In the ownership module you saw that a value is dropped at the end of its scope: its memory is freed. Drop lets you run your own code at that moment.
struct Connection {
id: u32,
}
impl Drop for Connection {
fn drop(&mut self) {
println!("closing connection {}", self.id);
}
}
fn main() {
let _a = Connection { id: 1 };
{
let _b = Connection { id: 2 };
println!("inner scope ends");
}
let c = Connection { id: 3 };
drop(c);
println!("main ends");
}
inner scope ends
closing connection 2
closing connection 3
main ends
closing connection 1
fn drop(&mut self)runs automatically._bis dropped when the inner block ends;_awhenmainends.- Values in the same scope are dropped in reverse order of creation (last in, first out), so things created later, which may depend on earlier ones, go away first.
drop(c)drops a value early. It's an ordinary function (std::mem::drop) that takes ownership and lets the value go out of scope.
This pattern is called RAII ("resource acquisition is initialization"): a value owns a resource and releases it in Drop, so you can't forget. Files close themselves, MutexGuards unlock their mutex, network connections hang up. You rarely write Drop for plain data; you write it for types that own something outside your program's memory.
Deref (briefly): * and automatic method calls
Deref lets a wrapper type act like a reference to what it contains. You use it all the time without noticing: a String derefs to str (that's why &String works where &str is expected) and a Vec<T> derefs to [T].
use std::ops::Deref;
struct Username(String);
impl Deref for Username {
type Target = str;
fn deref(&self) -> &str {
&self.0
}
}
fn greet(name: &str) -> String {
format!("hi {name}")
}
fn main() {
let u = Username(String::from("ferris"));
println!("{} {}", u.len(), u.to_uppercase());
println!("{}", greet(&u));
}
6 FERRIS
hi ferris
type Target = str;is what the wrapper derefs to, andderefreturns a reference to it.u.len():Usernamehas nolenmethod, so Rust derefs and findsstr::len.greet(&u): a&Usernameturns into a&strautomatically. This is deref coercion.
Use Deref for smart pointers and thin wrappers like this one. The smart pointers module comes back to Deref and Drop in depth.
Common mistakes
- Calling
.drop()yourself.c.drop()giveserror[E0040]: explicit use of destructor method, because Rust would then run it a second time at the end of the scope. Calldrop(c), which takes ownership. - Expecting creation order. Values in one scope are dropped last-created first, so the output of
Dropimpls can surprise you. CopyandDroptogether.error[E0184]: the trait `Copy` cannot be implemented for this type; the type has a destructor: with copies around, the cleanup would run several times.Derefto fake inheritance. "MyAdminderefs toUser, so it gets all ofUser's methods" confuses readers, and the Rust API guidelines advise against it. Use a field and forward the methods you need, or a shared trait.
Your task
Implement Drop for Connection so it prints close <id> when it's dropped. Don't change main. The program must print exactly:
open 1
open 2
using 2
close 2
open 3
close 1
end of main
close 3
Challenge 2
Fill in the blanks so Noisy prints drop <name> when it's dropped. Three values are created in one scope; notice the order they're dropped in. The program prints:
made all three
drop c
drop b
drop a
Challenge 3
A Tag wraps a String that's always lowercase. Write fn new(text: &str) -> Tag that lowercases the text, and implement Deref with Target = str, so a Tag can use every str method (len, starts_with...) and be passed where a &str is expected.