mirror of
https://github.com/darkicewolf50/RustBrock.git
synced 2025-06-15 13:04:18 -06:00
finihed ch15.3
This commit is contained in:
parent
78b0782ac9
commit
0052658af0
218
Drop Trait.md
Normal file
218
Drop Trait.md
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
# Running Code on Cleanup with the `Drop` Trait
|
||||||
|
The second trait improtant to the smart pointer pattern is `Drop`
|
||||||
|
|
||||||
|
This lets you customize what happens when a values is about to go out of scope.
|
||||||
|
|
||||||
|
You can provide an implementation for the `Drop` trait on any type and that code can be used to release resources like files or network connections.
|
||||||
|
|
||||||
|
`Drop` traits are almost always used when implementing a smart pointer but it is also useful in other cases/types.
|
||||||
|
|
||||||
|
For example when a `Box<T>` is dropped it will deallocate the space on the heap that the box points to.
|
||||||
|
|
||||||
|
In some languages the programmer must call code to free emory or resources when they finished using an instance of those types.
|
||||||
|
|
||||||
|
Some examples include file handles, sockets, or locks.
|
||||||
|
|
||||||
|
If they forget, the system might become overloaded and crash.
|
||||||
|
|
||||||
|
In Rust you can specify that a particluar bit of code that will run whenever a value gos out of scope.
|
||||||
|
|
||||||
|
The compler will insert this code automatically.
|
||||||
|
|
||||||
|
This results that you don't need to be careful about placing cleanup code everywhere in a program that an instance of a paritcular type is finished with.
|
||||||
|
|
||||||
|
You still won't leak resources.
|
||||||
|
|
||||||
|
The code that you want to run when a value goes out of scope goes in the implementation of the `Drop` trait.
|
||||||
|
|
||||||
|
The `Drop` trait requires you to implement one method named `drop` that takes a mutable refernce to `self`
|
||||||
|
|
||||||
|
to see when Rust calls `drop` we will implement `drop` with `println!` statements.
|
||||||
|
|
||||||
|
In this example it shows a `CustomSmartPoionter` struct whose only custom functionality is that it will print `Dropping CustomSmartPointer!` when the instance goes out of scope.
|
||||||
|
|
||||||
|
This is in order to show when Rust runs the `drop` function.
|
||||||
|
```rust
|
||||||
|
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.");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
The Drop Trait is included in the prelude so we don't need to bring it into scope.
|
||||||
|
|
||||||
|
Here we implemented the `Drop` trait on `CustomSmartPointer` and provide an implementation for the `drop` method that calls `println!`.
|
||||||
|
|
||||||
|
Here the body of the `drop` function is where you would place any logic that you wanted to run when an instance of your type goes out of cope.
|
||||||
|
|
||||||
|
We print some text here to demonstrate visually when Rust will call `drop`.
|
||||||
|
|
||||||
|
In `main` we create two instances of `CustomSmartPointer` and then print `CustomSmartPointers created`
|
||||||
|
|
||||||
|
At the end of `main` when the insatnces of `CustomSmartPointer` go out of scope.
|
||||||
|
|
||||||
|
Rust will call the code we put in the `drop` method.
|
||||||
|
|
||||||
|
This printed our final message.
|
||||||
|
|
||||||
|
Notice that we didn't need to call the `drop` method explicity.
|
||||||
|
|
||||||
|
Here is the output from that code
|
||||||
|
```
|
||||||
|
$ 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`!
|
||||||
|
```
|
||||||
|
|
||||||
|
Rust will automatically call `drop` for us when our instances go out of scope, calling the code speicifed by `drop`.
|
||||||
|
|
||||||
|
Varialbes are dropped in the reverse order they we created.
|
||||||
|
|
||||||
|
Here it means that `d` was dropped before `c`
|
||||||
|
|
||||||
|
This examples gives a visual guide on how the `drop` method works.
|
||||||
|
|
||||||
|
Usually you would specify the cleanup oe that your type needs to run rather than a print message.
|
||||||
|
|
||||||
|
## Dropping a Valu Early with `std::mem::drop`
|
||||||
|
This is not straightforward to to disable the automatic `drop` functionality.
|
||||||
|
|
||||||
|
This is usually unnecessary to disable `drop`.
|
||||||
|
|
||||||
|
The whole point of the `Drop` trait is that it is taken care of automatically.
|
||||||
|
|
||||||
|
Very rarely you might want to clean up a value early.
|
||||||
|
|
||||||
|
One example us when using smart pointers that manage your locks.
|
||||||
|
|
||||||
|
Here you may want to force the `drop` method that releases the lock so that other code in the same scope can aquire the lock.
|
||||||
|
|
||||||
|
Rust doesnt let you call the `Drop` trait `drop` method manually.
|
||||||
|
|
||||||
|
Here you would instead call the `std::mem::drop` fnction provided by te std library if you want to force a value to be dropped before the end of a scope.
|
||||||
|
|
||||||
|
If we try to call the `drop` method manually.
|
||||||
|
|
||||||
|
In this case we would get a compiler error
|
||||||
|
```rust
|
||||||
|
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("some data"),
|
||||||
|
};
|
||||||
|
println!("CustomSmartPointer created.");
|
||||||
|
c.drop();
|
||||||
|
println!("CustomSmartPointer dropped before the end of main.");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Here is the error output
|
||||||
|
```
|
||||||
|
$ 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 | 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
|
||||||
|
```
|
||||||
|
This error message states that we are not allowed to explicitly call `drop`.
|
||||||
|
|
||||||
|
The error mesage uses the term *destructor*, this is a general programming term for a function that cleans up an instance.
|
||||||
|
|
||||||
|
A *destructor* is analogous to a *constructor*, this creates an instance.
|
||||||
|
|
||||||
|
The `drop` function in Rust is one particular destructor.
|
||||||
|
|
||||||
|
Rust doesn't let us call `drop` explicity because Rust would still automatically call `drop` on the value at the end of `main`.
|
||||||
|
|
||||||
|
This would cause a *double free* error because Rust would be trying to clean up the same value twice.
|
||||||
|
|
||||||
|
We can't disable this automatic insertion of `drop` when a value goes out of scope.
|
||||||
|
|
||||||
|
We also can't call the `drop` method explicitly.
|
||||||
|
|
||||||
|
If we need to force a value to be cleaned up early, then we use the `std::mem::drop` function.
|
||||||
|
|
||||||
|
The `std::mem::drop` function is different from the `drop` method in the `Drop` trait.
|
||||||
|
|
||||||
|
We call this by passing the value we want to force to drop as an argument.
|
||||||
|
|
||||||
|
Here is the updated version with the function being in the prelude.
|
||||||
|
```rust
|
||||||
|
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("some data"),
|
||||||
|
};
|
||||||
|
println!("CustomSmartPointer created.");
|
||||||
|
drop(c);
|
||||||
|
println!("CustomSmartPointer dropped before the end of main.");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Here we get this output
|
||||||
|
```
|
||||||
|
$ 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.
|
||||||
|
```
|
||||||
|
The tet `Dropping CustomSmartPointer with data 'some data'!` is printed between the `CustomSmartPointer created.` and `CustomSmartPointer dropped before the end of main.` text.
|
||||||
|
|
||||||
|
This indicates that the `drop` method code is called to drop `c` at that point.
|
||||||
|
|
||||||
|
You can then use the code specified in a `Drop` 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 `Drop` trait and Rust's ownership system you dont have to remember to clean up because Rust will do it automatically.
|
||||||
|
|
||||||
|
You also dont have to worry about problems resulting from accidentally cleaning up valuess still in use.
|
||||||
|
|
||||||
|
The ownership system will ensure that references are always valid, also enusreing that `drop` gets called only once when the value is no longer being used.
|
1
Reference Counter Smart Pointer.md
Normal file
1
Reference Counter Smart Pointer.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
# `RC<T>`, the Reference Counted Smart Pointer
|
@ -41,7 +41,7 @@ Unlike an ordinary struct, smart pointers implements the `Deref` and `Drop` trai
|
|||||||
|
|
||||||
The `Deref` trait allows an instance of the smart pointer struct to behave like a reference so you can write your code to with either references or smart pointers. [Section Link Here](./Deref%20Trait.md)
|
The `Deref` trait allows an instance of the smart pointer struct to behave like a reference so you can write your code to with either references or smart pointers. [Section Link Here](./Deref%20Trait.md)
|
||||||
|
|
||||||
The `Drop` trait allows you to customize the code that is run when an instance of the smart pointer goes out of scope.
|
The `Drop` trait allows you to customize the code that is run when an instance of the smart pointer goes out of scope. [Section Link Here](./Drop%20Trait.md)
|
||||||
|
|
||||||
We will go over both traits and demonstrate why they are improtant to smart pointers.
|
We will go over both traits and demonstrate why they are improtant to smart pointers.
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ Many libraries have thier own smart pointers, and you can even write your own.
|
|||||||
|
|
||||||
The ones we will cover are the most common smart pointers in the std library:
|
The ones we will cover are the most common smart pointers in the std library:
|
||||||
- [`Box<T>` for allocating values on the heap](./Using%20Box%20on%20the%20Heap.md)
|
- [`Box<T>` for allocating values on the heap](./Using%20Box%20on%20the%20Heap.md)
|
||||||
- `Rc<T>`, a reference counting type that enables multiple ownership
|
- `Rc<T>`, a reference counting type that enables multiple ownership [Section Link Here](./Reference%20Counter%20Smart%20Pointer.md)
|
||||||
- `Ref<T>` and `RefMut<T>` this is accessed through `RefCell<T>`, a type that enforces the borrowing rules at runtime instead of compile time
|
- `Ref<T>` and `RefMut<T>` this is accessed through `RefCell<T>`, a type that enforces the borrowing rules at runtime instead of compile time
|
||||||
|
|
||||||
In additon we will also cover *interior mutability* patter where an immutable tpye exposes an API for mutating an interior value.
|
In additon we will also cover *interior mutability* patter where an immutable tpye exposes an API for mutating an interior value.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user