From 0052658af058f54f9d86586adc03a8c8a4b26341 Mon Sep 17 00:00:00 2001 From: darkicewolf50 Date: Tue, 4 Mar 2025 16:55:02 -0700 Subject: [PATCH] finihed ch15.3 --- Drop Trait.md | 218 +++++++++++++++++++++++++++++ Reference Counter Smart Pointer.md | 1 + Smart Pointers.md | 4 +- 3 files changed, 221 insertions(+), 2 deletions(-) create mode 100644 Drop Trait.md create mode 100644 Reference Counter Smart Pointer.md diff --git a/Drop Trait.md b/Drop Trait.md new file mode 100644 index 0000000..db1038a --- /dev/null +++ b/Drop Trait.md @@ -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` 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. \ No newline at end of file diff --git a/Reference Counter Smart Pointer.md b/Reference Counter Smart Pointer.md new file mode 100644 index 0000000..37e91b0 --- /dev/null +++ b/Reference Counter Smart Pointer.md @@ -0,0 +1 @@ +# `RC`, the Reference Counted Smart Pointer \ No newline at end of file diff --git a/Smart Pointers.md b/Smart Pointers.md index a9b892d..98e72dd 100644 --- a/Smart Pointers.md +++ b/Smart Pointers.md @@ -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 `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. @@ -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: - [`Box` for allocating values on the heap](./Using%20Box%20on%20the%20Heap.md) -- `Rc`, a reference counting type that enables multiple ownership +- `Rc`, a reference counting type that enables multiple ownership [Section Link Here](./Reference%20Counter%20Smart%20Pointer.md) - `Ref` and `RefMut` this is accessed through `RefCell`, 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.