mirror of
https://github.com/rust-lang/book.git
synced 2026-09-15 01:58:51 -04:00
Edits to 15-03
This commit is contained in:
@@ -70,6 +70,7 @@ ctrl
|
||||
Ctrl
|
||||
customizable
|
||||
CustomSmartPointer
|
||||
deallocate
|
||||
deallocated
|
||||
deallocating
|
||||
deallocation
|
||||
|
||||
@@ -1,32 +1,53 @@
|
||||
## 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.
|
||||
The second trait important to the smart pointer pattern is `Drop`, which lets
|
||||
us customize what happens when a value is about to go out of scope. We can
|
||||
provide an implementation for the `Drop` trait on any type, and the code we
|
||||
specify can be used to release resources like files or network connections.
|
||||
We're introducing `Drop` in the context of smart pointers because the
|
||||
functionality of the `Drop` trait is almost always used when implementing a
|
||||
smart pointer. For example, `Box<T>` customizes `Drop` in order to deallocate
|
||||
the space on the heap that the box points to.
|
||||
|
||||
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!
|
||||
In some languages, the programmer must call code to free memory or resources
|
||||
every time they finish using an instance of a smart pointer. If they forget,
|
||||
the system might become overloaded and crash. In Rust, we can specify that a
|
||||
particular bit of code should be run whenever a value goes out of scope, and
|
||||
the compiler will insert this code automatically.
|
||||
|
||||
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`.
|
||||
<!-- Are we saying that any code can be run, and that we can use that to clean
|
||||
up, or that this code that can be run is specifically always for clean up? -->
|
||||
<!-- I don't understand what the difference between those two choices are?
|
||||
/Carol -->
|
||||
|
||||
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:
|
||||
This means we don't need be careful about placing clean up code everywhere in a
|
||||
program that an instance of a particular type is finished with, but we still
|
||||
won't leak resources!
|
||||
|
||||
We specify the code to run when a value goes out of scope by implementing the
|
||||
`Drop` trait. The `Drop` trait requires us to implement one method named `drop`
|
||||
that takes a mutable reference to `self`. In order to be able to see when Rust
|
||||
calls `drop`, let's implement `drop` with `println!` statements for now.
|
||||
|
||||
<!-- Why are we showing this as an example and not an example of it being used
|
||||
for clean up? -->
|
||||
<!-- To demonstrate the mechanics of implementing the trait and showing when
|
||||
this code gets run. It's hard to experience the cleaning up unless we print
|
||||
something. /Carol -->
|
||||
|
||||
Listing 15-8 shows a `CustomSmartPointer` struct whose only custom
|
||||
functionality is that it will print out `Dropping CustomSmartPointer!` when the
|
||||
instance goes out of scope. This will demonstrate when Rust runs the `drop`
|
||||
function:
|
||||
|
||||
<!-- Is this below just telling us how to adapt it for cleaning up instead?
|
||||
Maybe save it for when we have context for it? Instead of a `println!`
|
||||
statement, you'd fill in `drop` with whatever cleanup code your smart pointer
|
||||
needs to run: -->
|
||||
<!-- This is demonstrating what we need to do to use `Drop`, without getting
|
||||
into the complexities of what "cleaning up" might mean yet, just to give the
|
||||
reader an idea of when this code gets called and that it gets called
|
||||
automatically. We're building up to cleaning up. /Carol -->
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@@ -44,40 +65,66 @@ impl Drop for CustomSmartPointer {
|
||||
fn main() {
|
||||
let c = CustomSmartPointer { data: String::from("some data") };
|
||||
println!("CustomSmartPointer created.");
|
||||
println!("Wait for it...");
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 15-8: A `CustomSmartPointer` struct that
|
||||
implements the `Drop` trait, where we could put code that would clean up after
|
||||
the `CustomSmartPointer`.</span>
|
||||
implements the `Drop` trait, where we would put our clean up code.</span>
|
||||
|
||||
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.
|
||||
The `Drop` trait is included in the prelude, so we don't need to import it. We
|
||||
implement the `Drop` trait on `CustomSmartPointer`, and provide an
|
||||
implementation for the `drop` method that calls `println!`. The body of the
|
||||
`drop` function is where you'd put any logic that you wanted to run when an
|
||||
instance of your type goes out of scope. We're choosing to print out some text
|
||||
here in order to demonstrate when Rust will call `drop`.
|
||||
|
||||
When we run this program, we’ll see:
|
||||
<!-- Where you'd put this code, or where this code would be called? It seems
|
||||
laborious to write this clean up code wherever there's a print call? -->
|
||||
<!-- I'm not sure how you concluded that from what we had here, could you
|
||||
elaborate? /Carol -->
|
||||
|
||||
In `main`, we create a new instance of `CustomSmartPointer` and then print out
|
||||
`CustomSmartPointer created.`. At the end of `main`, our instance of
|
||||
`CustomSmartPointer` will go out of scope, and Rust will call the code we put
|
||||
in the `drop` method, printing our final message. Note that we didn't need to
|
||||
call the `drop` method explicitly.
|
||||
|
||||
When we run this program, we'll see the following output:
|
||||
|
||||
```text
|
||||
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.
|
||||
Rust automatically called `drop` for us when our instance went out of scope,
|
||||
calling the code we specified. This is just to give you a visual guide to how
|
||||
the drop method works, but usually you would specify the cleanup code that your
|
||||
type needs to run rather than a print message.
|
||||
|
||||
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:
|
||||
<!-- Can you wrap this example up by saying what you would actually put in a
|
||||
drop method and why?-->
|
||||
<!-- Done /Carol -->
|
||||
|
||||
#### Dropping a Value Early with `std::mem::drop`
|
||||
|
||||
<!-- is this a new method from Drop or the same method? -->
|
||||
<!-- This is a new function. /Carol -->
|
||||
|
||||
Rust inserts the call to `drop` automatically when a value goes out of scope,
|
||||
and there's no way to disable this functionality if we want to force a value to
|
||||
clean itself up early. This isn't usually necessary; the whole point of the
|
||||
`Drop` trait is that it's taken care of automatically for us. Occasionally you
|
||||
may find that you want to clean up a value early. One example is when using
|
||||
smart pointers that manage locks; you may want to force the `drop` method that
|
||||
releases the lock to run so that other code in the same scope can acquire the
|
||||
lock. First, let's see what happens if we try to call the `Drop` trait's `drop`
|
||||
method ourselves by modifying the `main` function from Listing 15-8 as shown in
|
||||
Listing 15-9:
|
||||
|
||||
<!-- Above: I'm not following why we are doing this, if it's not necessary and
|
||||
we aren't going to cover it now anyway -- can you lay out why we're discussing
|
||||
this here? -->
|
||||
<!-- Done. /Carol -->
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@@ -85,56 +132,101 @@ that it’s possible, and `std::mem::drop` is in the prelude so we can just call
|
||||
fn main() {
|
||||
let c = CustomSmartPointer { data: String::from("some data") };
|
||||
println!("CustomSmartPointer created.");
|
||||
drop(c);
|
||||
println!("Wait for it...");
|
||||
c.drop();
|
||||
println!("CustomSmartPointer dropped before the end of main.");
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 15-9: Calling `std::mem::drop` to explicitly drop
|
||||
a value before it goes out of scope</span>
|
||||
<span class="caption">Listing 15-9: Attempting to call the `drop` method from
|
||||
the `Drop` trait manually to clean up early</span>
|
||||
|
||||
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...`:
|
||||
If we try to compile this, we'll get this error:
|
||||
|
||||
```text
|
||||
error[E0040]: explicit use of destructor method
|
||||
--> src/main.rs:15:7
|
||||
|
|
||||
15 | c.drop();
|
||||
| ^^^^ explicit destructor calls not allowed
|
||||
```
|
||||
|
||||
This error message says we're not allowed to explicitly call `drop`. The error
|
||||
message uses the term *destructor*, which is the general programming term for a
|
||||
function that cleans up an instance. A *destructor* is analogous to a
|
||||
*constructor* that creates an instance. The `drop` function in Rust is one
|
||||
particular destructor.
|
||||
|
||||
Rust doesn't let us call `drop` explicitly because Rust would still
|
||||
automatically call `drop` on the value at the end of `main`, and this would be
|
||||
a *double free* error since Rust would be trying to clean up the same value
|
||||
twice.
|
||||
|
||||
Because we can't disable the automatic insertion of `drop` when a value goes
|
||||
out of scope, and we can't call the `drop` method explicitly, if we need to
|
||||
force a value to be cleaned up early, we can use the `std::mem::drop` function.
|
||||
|
||||
The `std::mem::drop` function is different than the `drop` method in the `Drop`
|
||||
trait. We call it by passing the value we want to force to be dropped early as
|
||||
an argument. `std::mem::drop` is in the prelude, so we can modify `main` from
|
||||
Listing 15-8 to call the `drop` function as shown in Listing 15-10:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
# 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.");
|
||||
drop(c);
|
||||
println!("CustomSmartPointer dropped before the end of main.");
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 15-10: Calling `std::mem::drop` to explicitly
|
||||
drop a value before it goes out of scope</span>
|
||||
|
||||
Running this code will print the following:
|
||||
|
||||
```text
|
||||
CustomSmartPointer created.
|
||||
Dropping CustomSmartPointer!
|
||||
Wait for it...
|
||||
CustomSmartPointer dropped before the end of main.
|
||||
```
|
||||
|
||||
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:
|
||||
<!-- What's the destructor code, here? We haven't mentioned that before, not in
|
||||
this chapter in any case -->
|
||||
<!-- I added a definition for destructor a few paragraphs above, the first time
|
||||
we see it in an error message. /Carol -->
|
||||
|
||||
```rust
|
||||
pub mod std {
|
||||
pub mod mem {
|
||||
pub fn drop<T>(x: T) { }
|
||||
}
|
||||
}
|
||||
```
|
||||
The `Dropping CustomSmartPointer!` is printed between `CustomSmartPointer
|
||||
created.` and `CustomSmartPointer dropped before the end of main.`, showing
|
||||
that the `drop` method code is called to drop `c` at that point.
|
||||
|
||||
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.
|
||||
<!-- How does this show that the destructor code (is that drop?) is called? Is
|
||||
this correct, above?-->
|
||||
<!-- The order of what gets printed shows that the drop code is called.
|
||||
/Carol-->
|
||||
|
||||
Code specified in a `Drop` trait implementation can be used for many reasons to
|
||||
Code specified in a `Drop` trait implementation can be used in many ways 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.
|
||||
allocator, for instance! With the `Drop` trait and Rust's ownership system, you
|
||||
don't have to remember to clean up after yourself, Rust takes care of it
|
||||
automatically.
|
||||
|
||||
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.
|
||||
We also don't have to worry about accidentally cleaning up values still in use
|
||||
because that would cause a compiler error: the ownership system that makes sure
|
||||
references are always valid will also make sure that `drop` only gets called
|
||||
once 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.
|
||||
|
||||
Reference in New Issue
Block a user