mirror of
https://github.com/darkicewolf50/RustBrock.git
synced 2025-08-01 07:40:54 -06:00
Compare commits
2 Commits
856e0e992a
...
0052658af0
Author | SHA1 | Date | |
---|---|---|---|
0052658af0 | |||
78b0782ac9 |
@ -221,3 +221,101 @@ This then matches the `5` in `assert_eq!`
|
||||
|
||||
For example a deref coercion can convert `&String` to `&str` because `String` implemented the `Deref` trait such that it returns `&str`.
|
||||
|
||||
Deref coercion is a convenience that Rust perfomrs on args to functions and methods.
|
||||
|
||||
This only works on tpyes that implement the `Deref` trait.
|
||||
|
||||
It happens automatically when we pass a reference to a particular type's value as an arg to a function or method that doesn't match the parameter type in the function or method definition.
|
||||
|
||||
A sequence of calls to the `deref` method converts the type we provided into the type the parameter needs.
|
||||
|
||||
Deref coercion was added to Rust so that rogrammers writing function and method calls don't need to add as many explicit references and dereferences with `&` and `*`.
|
||||
|
||||
This feature also lets us write more code that can code that can work for either references or smart pointers.
|
||||
|
||||
To see deref coercion in action we will use `MyBox<T>` type defined previously with the implementation of `Deref` as well.
|
||||
|
||||
This example shows the definition of a function that has a string slice parameter
|
||||
```rust
|
||||
fn hello(name: &str) {
|
||||
println!("Hello, {name}!");
|
||||
}
|
||||
```
|
||||
We can call the `hello` function with a string slice as an arg.
|
||||
|
||||
Such as `hello("Rust");` as an example.
|
||||
|
||||
With Deref coercion makes it possible to call `hello` with a reference to a value of type `MyBox<String>`
|
||||
|
||||
Here is an example of this
|
||||
```rust
|
||||
fn main() {
|
||||
let m = MyBox::new(String::from("Rust"));
|
||||
hello(&m);
|
||||
}
|
||||
```
|
||||
Here we call the `hello` function with the arg `&m`, which is a reference to a `MyBox<String>` value.
|
||||
|
||||
Due to us implementing the `Deref` trait on `MyBox<T>`, Rust can turn `&MyBox<String>` into a `&String` by calling deref.
|
||||
|
||||
The std library then proves an implementation of `Deref` on `String` that reutnrs a string slice.
|
||||
|
||||
This is in the API docs for `Deref`.
|
||||
|
||||
Rust then calls `deref` again to turn the `&String` into `&str`. This matches the `hello` function definition.
|
||||
|
||||
If Rust didn't implement deref coercion we would have to wrtie does like this instead to call `hello` with a value of type `&MyBox<String>`
|
||||
```rust
|
||||
fn main() {
|
||||
let m = MyBox::new(String::from("Rust"));
|
||||
hello(&(*m)[..]);
|
||||
}
|
||||
```
|
||||
|
||||
The `(*m)` dereferences the `MyBox<String>` into a `String`.
|
||||
|
||||
The `&` and `[..]` takes a string slice of the `String` that is equal to the whole string to match the signature of `hello`.
|
||||
|
||||
This code without deref coercions is much harder to read, wrtie and understand, escpially with all of the symbols involved.
|
||||
|
||||
Deref coercion allows Rusts to handle all of these conversions automatically.
|
||||
|
||||
When the `Deref` trait is defined for the types involved.
|
||||
|
||||
Rust will analyze the types and use `Deref::deref` as many times as necessary to get a reference to match the param type.
|
||||
|
||||
The number of times that `Deref::deref` needs to be inserted is resolved at compile time.
|
||||
|
||||
There is no runtime penalty for taking advantage of deref coercion.
|
||||
|
||||
## How Deref Coercion Interacts with Mutability
|
||||
Similar to how you would use `Deref` to override the `*` operator on immutable references, you can use the `DerefMut` trait to override the `*` operator on mutable references.
|
||||
|
||||
Rust does deref coercion when it finds tpyes and trait implelemtations in there cases
|
||||
- From `&T` to `&U` when `T: Deref<Target=U>`
|
||||
- From `&mut T` to `&mut U` when `T: DerefMut<Target=U>`
|
||||
- From `&mut T` to `&U` when `T: Deref<Target=U>`
|
||||
|
||||
The fist two cases are the same expect that the second implements mutablility.
|
||||
|
||||
The first one states that if you have a `&T` and `T` implements `Deref` to some type `U` you can get a `&U` transparently
|
||||
|
||||
The second states that the smae deref coercion happens for mutable references.
|
||||
|
||||
The third one is tricker.
|
||||
|
||||
Rust will also coerce a mutable reference to an immutable one.
|
||||
|
||||
The reverse is *not* possible: immutable references will never coerce to mutable refernces.
|
||||
|
||||
This is due to the borrowing rules, if you have a mutable reference, that mutable reference must be the only reference to that data.
|
||||
|
||||
(The program will not compile if otherwise)
|
||||
|
||||
Converting one mutable reference to one immutable reference will never break the borrowing rules.
|
||||
|
||||
Converting an immutable reference to a mutable reference would require that the intial immutable reference is the only immutable reference to that data.
|
||||
|
||||
Borrowing rukes don't gaurantee that.
|
||||
|
||||
Therefore Rust can't make that assumption that converting an immutable reference to a mutable reference is possible.
|
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 `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<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
|
||||
|
||||
In additon we will also cover *interior mutability* patter where an immutable tpye exposes an API for mutating an interior value.
|
||||
|
Reference in New Issue
Block a user