Using the Drop Trait in Rust to Manage End-of-Scope Logic
The Drop
trait in Rust can be used for automatic resource release when a value goes out of scope. It can also be handy when you want to execute similar logic that has a "start" and "end" point.
The official documentation gives a great introduction to the Drop
trait:
When a value is no longer needed, Rust will run a “destructor” on that value. The most common way that a value is no longer needed is when it goes out of scope. This destructor consists of two components:
- A call to Drop::drop for that value, if this special Drop trait is implemented for its type.
- The automatically generated “drop glue” which recursively calls the destructors of all the fields of this value.
The trait can be implemented by:
pub trait Drop {
// Required method
fn drop(&mut self);
}
TLDR: The Drop
trait is used to define the behavior when a value goes out of scope, and it is done recursively for all fields of the value.
This can be useful in scenarios when that out of scope is a signal for an end of a process. For example:
- Opening a file and closing it when the value goes out of scope.
- Acquiring a lock and releasing it when the value goes out of scope.
- Emitting a start and end message/event when the value goes out of scope.
Here is an example of how the Drop
trait can be used to log the start and end of a process.
pub trait Logger {
fn start_log(&self) {
println!("Start of logging");
}
fn log(&self, message: &str) {
println!("{}", message);
}
fn end_log(&self) {
println!("End of logging");
}
}
pub struct MyLogger;
impl Logger for MyLogger {}
impl MyLogger {
pub fn new() -> Self {
let logger = MyLogger;
logger.start_log();
logger
}
}
impl Drop for MyLogger {
fn drop(&mut self) {
self.end_log();
}
}
Then, you can use the MyLogger
struct like this:
fn main() {
let logger = MyLogger::new(); // Automatically logs the start of logging
logger.log("Do something");
} // Automatically logs the end of logging
When the logger
is initialized, the start_log
method will be called. When the value goes out of scope, the end_log
method will be called, and the final output will be:
Start of logging
Do something
End of logging
You can play around with the above code in the Rust Playground.
A side note is that in practice, testing code with the Drop
trait can be more complicated than expected. Though we can trigger the Drop
trait via std::mem::drop
, the lifetime of the underlying value may be impacted, and you might have a hard time asserting something that is gone when dropped. I will cover this in a future post as it requires a more complex example that compares between the usage of Box, generics and dynamic dispatching.