196 lines
11 KiB
HTML
196 lines
11 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>Running Code on Cleanup with the Drop Trait</title>
|
||
</head>
|
||
<body>
|
||
<h2 id="running-code-on-cleanup-with-the-drop-trait"><a class="header" href="#running-code-on-cleanup-with-the-drop-trait">Running Code on Cleanup with the <code>Drop</code> Trait</a></h2>
|
||
<p>The second trait important to the smart pointer pattern is <code>Drop</code>, which lets
|
||
you customize what happens when a value is about to go out of scope. You can
|
||
provide an implementation for the <code>Drop</code> trait on any type, and that code can
|
||
be used to release resources like files or network connections.</p>
|
||
<p>We’re introducing <code>Drop</code> in the context of smart pointers because the
|
||
functionality of the <code>Drop</code> trait is almost always used when implementing a
|
||
smart pointer. For example, when a <code>Box<T></code> is dropped, it will deallocate the
|
||
space on the heap that the box points to.</p>
|
||
<p>In some languages, for some types, the programmer must call code to free memory
|
||
or resources every time they finish using an instance of those types. Examples
|
||
include file handles, sockets, and locks. If the programmer forgets, the system
|
||
might become overloaded and crash. In Rust, you can specify that a particular
|
||
bit of code be run whenever a value goes out of scope, and the compiler will
|
||
insert this code automatically. As a result, you don’t need to be careful about
|
||
placing cleanup code everywhere in a program that an instance of a particular
|
||
type is finished with—you still won’t leak resources!</p>
|
||
<p>You specify the code to run when a value goes out of scope by implementing the
|
||
<code>Drop</code> trait. The <code>Drop</code> trait requires you to implement one method named
|
||
<code>drop</code> that takes a mutable reference to <code>self</code>. To see when Rust calls <code>drop</code>,
|
||
let’s implement <code>drop</code> with <code>println!</code> statements for now.</p>
|
||
<p>Listing 15-14 shows a <code>CustomSmartPointer</code> struct whose only custom
|
||
functionality is that it will print <code>Dropping CustomSmartPointer!</code> when the
|
||
instance goes out of scope, to show when Rust runs the <code>drop</code> method.</p>
|
||
<figure class="listing" id="listing-15-14">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<pre class="playground"><code class="language-rust edition2024">struct CustomSmartPointer {
|
||
data: String,
|
||
}
|
||
|
||
impl Drop for CustomSmartPointer {
|
||
fn drop(&mut self) {
|
||
println!("Dropping CustomSmartPointer with data `{}`!", self.data);
|
||
}
|
||
}
|
||
|
||
fn main() {
|
||
let c = CustomSmartPointer {
|
||
data: String::from("my stuff"),
|
||
};
|
||
let d = CustomSmartPointer {
|
||
data: String::from("other stuff"),
|
||
};
|
||
println!("CustomSmartPointers created");
|
||
}</code></pre>
|
||
<figcaption><a href="#listing-15-14">Listing 15-14</a>: A <code>CustomSmartPointer</code> struct that implements the <code>Drop</code> trait where we would put our cleanup code</figcaption>
|
||
</figure>
|
||
<p>The <code>Drop</code> trait is included in the prelude, so we don’t need to bring it into
|
||
scope. We implement the <code>Drop</code> trait on <code>CustomSmartPointer</code> and provide an
|
||
implementation for the <code>drop</code> method that calls <code>println!</code>. The body of the
|
||
<code>drop</code> method is where you would place any logic that you wanted to run when an
|
||
instance of your type goes out of scope. We’re printing some text here to
|
||
demonstrate visually when Rust will call <code>drop</code>.</p>
|
||
<p>In <code>main</code>, we create two instances of <code>CustomSmartPointer</code> and then print
|
||
<code>CustomSmartPointers created</code>. At the end of <code>main</code>, our instances of
|
||
<code>CustomSmartPointer</code> will go out of scope, and Rust will call the code we put
|
||
in the <code>drop</code> method, printing our final message. Note that we didn’t need to
|
||
call the <code>drop</code> method explicitly.</p>
|
||
<p>When we run this program, we’ll see the following output:</p>
|
||
<pre><code class="language-console">$ cargo run
|
||
Compiling drop-example v0.1.0 (file:///projects/drop-example)
|
||
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.60s
|
||
Running `target/debug/drop-example`
|
||
CustomSmartPointers created
|
||
Dropping CustomSmartPointer with data `other stuff`!
|
||
Dropping CustomSmartPointer with data `my stuff`!
|
||
</code></pre>
|
||
<p>Rust automatically called <code>drop</code> for us when our instances went out of scope,
|
||
calling the code we specified. Variables are dropped in the reverse order of
|
||
their creation, so <code>d</code> was dropped before <code>c</code>. This example’s purpose is to
|
||
give you a visual guide to how the <code>drop</code> method works; usually you would
|
||
specify the cleanup code that your type needs to run rather than a print
|
||
message.</p>
|
||
<!-- Old headings. Do not remove or links may break. -->
|
||
<p><a id="dropping-a-value-early-with-std-mem-drop"></a></p>
|
||
<p>Unfortunately, it’s not straightforward to disable the automatic <code>drop</code>
|
||
functionality. Disabling <code>drop</code> isn’t usually necessary; the whole point of the
|
||
<code>Drop</code> trait is that it’s taken care of automatically. Occasionally, however,
|
||
you might want to clean up a value early. One example is when using smart
|
||
pointers that manage locks: You might want to force the <code>drop</code> method that
|
||
releases the lock so that other code in the same scope can acquire the lock.
|
||
Rust doesn’t let you call the <code>Drop</code> trait’s <code>drop</code> method manually; instead,
|
||
you have to call the <code>std::mem::drop</code> function provided by the standard library
|
||
if you want to force a value to be dropped before the end of its scope.</p>
|
||
<p>Trying to call the <code>Drop</code> trait’s <code>drop</code> method manually by modifying the
|
||
<code>main</code> function from Listing 15-14 won’t work, as shown in Listing 15-15.</p>
|
||
<figure class="listing" id="listing-15-15">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<pre><code class="language-rust ignore does_not_compile"><span class="boring">struct CustomSmartPointer {
|
||
</span><span class="boring"> data: String,
|
||
</span><span class="boring">}
|
||
</span><span class="boring">
|
||
</span><span class="boring">impl Drop for CustomSmartPointer {
|
||
</span><span class="boring"> fn drop(&mut self) {
|
||
</span><span class="boring"> println!("Dropping CustomSmartPointer with data `{}`!", self.data);
|
||
</span><span class="boring"> }
|
||
</span><span class="boring">}
|
||
</span><span class="boring">
|
||
</span>fn main() {
|
||
let c = CustomSmartPointer {
|
||
data: String::from("some data"),
|
||
};
|
||
println!("CustomSmartPointer created");
|
||
c.drop();
|
||
println!("CustomSmartPointer dropped before the end of main");
|
||
}</code></pre>
|
||
<figcaption><a href="#listing-15-15">Listing 15-15</a>: Attempting to call the <code>drop</code> method from the <code>Drop</code> trait manually to clean up early</figcaption>
|
||
</figure>
|
||
<p>When we try to compile this code, we’ll get this error:</p>
|
||
<pre><code class="language-console">$ cargo run
|
||
Compiling drop-example v0.1.0 (file:///projects/drop-example)
|
||
error[E0040]: explicit use of destructor method
|
||
--> src/main.rs:16:7
|
||
|
|
||
16 | c.drop();
|
||
| ^^^^ explicit destructor calls not allowed
|
||
|
|
||
help: consider using `drop` function
|
||
|
|
||
16 - c.drop();
|
||
16 + drop(c);
|
||
|
|
||
|
||
For more information about this error, try `rustc --explain E0040`.
|
||
error: could not compile `drop-example` (bin "drop-example") due to 1 previous error
|
||
</code></pre>
|
||
<p>This error message states that we’re not allowed to explicitly call <code>drop</code>. The
|
||
error message uses the term <em>destructor</em>, which is the general programming term
|
||
for a function that cleans up an instance. A <em>destructor</em> is analogous to a
|
||
<em>constructor</em>, which creates an instance. The <code>drop</code> function in Rust is one
|
||
particular destructor.</p>
|
||
<p>Rust doesn’t let us call <code>drop</code> explicitly, because Rust would still
|
||
automatically call <code>drop</code> on the value at the end of <code>main</code>. This would cause a
|
||
double free error because Rust would be trying to clean up the same value twice.</p>
|
||
<p>We can’t disable the automatic insertion of <code>drop</code> when a value goes out of
|
||
scope, and we can’t call the <code>drop</code> method explicitly. So, if we need to force
|
||
a value to be cleaned up early, we use the <code>std::mem::drop</code> function.</p>
|
||
<p>The <code>std::mem::drop</code> function is different from the <code>drop</code> method in the <code>Drop</code>
|
||
trait. We call it by passing as an argument the value we want to force-drop.
|
||
The function is in the prelude, so we can modify <code>main</code> in Listing 15-15 to
|
||
call the <code>drop</code> function, as shown in Listing 15-16.</p>
|
||
<figure class="listing" id="listing-15-16">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">struct CustomSmartPointer {
|
||
</span><span class="boring"> data: String,
|
||
</span><span class="boring">}
|
||
</span><span class="boring">
|
||
</span><span class="boring">impl Drop for CustomSmartPointer {
|
||
</span><span class="boring"> fn drop(&mut self) {
|
||
</span><span class="boring"> println!("Dropping CustomSmartPointer with data `{}`!", self.data);
|
||
</span><span class="boring"> }
|
||
</span><span class="boring">}
|
||
</span><span class="boring">
|
||
</span>fn main() {
|
||
let c = CustomSmartPointer {
|
||
data: String::from("some data"),
|
||
};
|
||
println!("CustomSmartPointer created");
|
||
drop(c);
|
||
println!("CustomSmartPointer dropped before the end of main");
|
||
}</code></pre>
|
||
<figcaption><a href="#listing-15-16">Listing 15-16</a>: Calling <code>std::mem::drop</code> to explicitly drop a value before it goes out of scope</figcaption>
|
||
</figure>
|
||
<p>Running this code will print the following:</p>
|
||
<pre><code class="language-console">$ cargo run
|
||
Compiling drop-example v0.1.0 (file:///projects/drop-example)
|
||
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.73s
|
||
Running `target/debug/drop-example`
|
||
CustomSmartPointer created
|
||
Dropping CustomSmartPointer with data `some data`!
|
||
CustomSmartPointer dropped before the end of main
|
||
</code></pre>
|
||
<p>The text <code>Dropping CustomSmartPointer with data `some data`!</code> is printed
|
||
between the <code>CustomSmartPointer created</code> and <code>CustomSmartPointer dropped before the end of main</code> text, showing that the <code>drop</code> method code is called to drop
|
||
<code>c</code> at that point.</p>
|
||
<p>You can use code specified in a <code>Drop</code> trait implementation in many ways to
|
||
make cleanup convenient and safe: For instance, you could use it to create your
|
||
own memory allocator! With the <code>Drop</code> trait and Rust’s ownership system, you
|
||
don’t have to remember to clean up, because Rust does it automatically.</p>
|
||
<p>You also don’t have to worry about problems resulting from accidentally
|
||
cleaning up values still in use: The ownership system that makes sure
|
||
references are always valid also ensures that <code>drop</code> gets called only once when
|
||
the value is no longer being used.</p>
|
||
<p>Now that we’ve examined <code>Box<T></code> and some of the characteristics of smart
|
||
pointers, let’s look at a few other smart pointers defined in the standard
|
||
library.</p>
|
||
</body>
|
||
</html>
|