finished ch15.2

This commit is contained in:
darkicewolf50 2025-03-04 14:13:02 -07:00
parent 856e0e992a
commit 78b0782ac9

View File

@ -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`. 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.