Skip to main content

Using the Drop Trait in Rust to Manage End-of-Scope Logic

· 3 min read

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.