The `Drop` Trait Runs Code on Cleanup - The Rust Programming Language

## The `Drop` Trait Runs Code on Cleanup

The other trait that’s important to the smart pointer pattern is the `Drop` trait. `Drop` lets us run some code when a value is about to go out of scope. Smart pointers perform important cleanup when being dropped, like deallocating memory or decrementing a reference count. More generally, data types can manage resources beyond memory, like files or network connections, and use `Drop` to release those resources when our code is done with them. We’re discussing `Drop` in the context of smart pointers, though, because the functionality of the `Drop` trait is almost always used when implementing smart pointers.

In some other languages, we have to remember to call code to free the memory or resource every time we finish using an instance of a smart pointer. If we forget, the system our code is running on might get overloaded and crash. In Rust, we can specify that some code should be run when a value goes out of scope, and the compiler will insert this code automatically. That means we don’t need to remember to put this code everywhere we’re done with an instance of these types, but we still won’t leak resources!

The way we specify code should be run when a value goes out of scope is by implementing the `Drop` trait. The `Drop` trait requires us to implement one method named `drop` that takes a mutable reference to `self`.

Listing 15-8 shows a `CustomSmartPointer` struct that doesn’t actually do anything, but we’re printing out `CustomSmartPointer created.` right after we create an instance of the struct and `Dropping CustomSmartPointer!` when the instance goes out of scope so that we can see when each piece of code gets run. Instead of a `println!` statement, you’d fill in `drop` with whatever cleanup code your smart pointer needs to run:

Filename: src/main.rs

    struct CustomSmartPointer {
        data: String,
    }

    impl Drop for CustomSmartPointer {
        fn drop(&mut self) {
            println!("Dropping CustomSmartPointer!");
        }
    }

    fn main() {
        let c = CustomSmartPointer { data: String::from("some data") };
        println!("CustomSmartPointer created.");
        println!("Wait for it...");
    }

Listing 15-8: A `CustomSmartPointer` struct that implements the `Drop` trait, where we could put code that would clean up after the `CustomSmartPointer`.

The `Drop` trait is in the prelude, so we don’t need to import it. The `drop` method implementation calls the `println!`; this is where you’d put the actual code needed to close the socket. In `main`, we create a new instance of `CustomSmartPointer` then print out `CustomSmartPointer created.` to be able to see that our code got to that point at runtime. At the end of `main`, our instance of `CustomSmartPointer` will go out of scope. Note that we didn’t call the `drop` method explicitly.

When we run this program, we’ll see:

    CustomSmartPointer created.
    Wait for it...
    Dropping CustomSmartPointer!

printed to the screen, which shows that Rust automatically called `drop` for us when our instance went out of scope.

We can use the `std::mem::drop` function to drop a value earlier than when it goes out of scope. This isn’t usually necessary; the whole point of the `Drop` trait is that it’s taken care of automatically for us. We’ll see an example of a case when we’ll need to drop a value earlier than when it goes out of scope in Chapter 16 when we’re talking about concurrency. For now, let’s just see that it’s possible, and `std::mem::drop` is in the prelude so we can just call `drop` as shown in Listing 15-9:

Filename: src/main.rs

    fn main() {
        let c = CustomSmartPointer { data: String::from("some data") };
        println!("CustomSmartPointer created.");
        drop(c);
        println!("Wait for it...");
    }

Listing 15-9: Calling `std::mem::drop` to explicitly drop a value before it goes out of scope

Running this code will print the following, showing that the destructor code is called since `Dropping CustomSmartPointer!` is printed between `CustomSmartPointer created.` and `Wait for it...`:

    CustomSmartPointer created.
    Dropping CustomSmartPointer!
    Wait for it...

Note that we aren’t allowed to call the `drop` method that we defined directly: if we replaced `drop(c)` in Listing 15-9 with `c.drop()`, we’ll get a compiler error that says `explicit destructor calls not allowed`. We’re not allowed to call `Drop::drop` directly because when Rust inserts its call to `Drop::drop` automatically when the value goes out of scope, then the value would get dropped twice. Dropping a value twice could cause an error or corrupt memory, so Rust doesn’t let us. Instead, we can use `std::mem::drop`, whose definition is:

    # #![allow(unused_variables)]
    #fn main() {
    pub mod std {
        pub mod mem {
            pub fn drop<T>(x: T) { }
        }
    }

    #}

This function is generic over any type `T`, so we can pass any value to it. The function doesn’t actually have anything in its body, so it doesn’t use its parameter. The reason this empty function is useful is that `drop` takes ownership of its parameter, which means the value in `x` gets dropped at the end of this function when `x` goes out of scope.

Code specified in a `Drop` trait implementation can be used for many reasons to make cleanup convenient and safe: we could use it to create our own memory allocator, for instance! By using the `Drop` trait and Rust’s ownership system, we don’t have to remember to clean up after ourselves since Rust takes care of it automatically. We’ll get compiler errors if we write code that would clean up a value that’s still in use, since the ownership system that makes sure references are always valid will also make sure that `drop` only gets called one time when the value is no longer being used.

Now that we’ve gone over `Box<T>` and some of the characteristics of smart pointers, let’s talk about a few other smart pointers defined in the standard library that add different kinds of useful functionality.