# Treating Smart Pointers Like Regular References with the `Deref` Trait Implmenting the `Deref` trait allows you to customize the behavior of the *dereference operator* `*`. This is not the multiplication or glob operator Implementing `Deref` in a way such that a smart pointer can be treated like a regular reference. You can wirte code that operators on references and use that code with smart pointers as well. Now lets look at the dereference operator works with regualr references. Next we will try to define a custom type that behaves like `Box`. Then we will see why the dereference operator doesnt work like a reference on our newly behaves lik `Box`. We will explore how implementing the `Deref` trait makes it possible for smart pointers to work in a way that is simialr to references. Finally we will lokk at Rust's *deref coercion* feature and how it let us work with either references or smart pointers. Note: The big difference between the `MyBox` type, the custom type we are about to build and the real `Box`. Our version will not store its data on the heap. We are focusing on the `Deref` trait, so where the data is actualy stored is less improtant than the pointer-like behavior. ## Following the Pointer to the Value A regular reference is a type of pointer. One way to think about a pointer is, an arrow to a value stored somewhere else. In this example we create a reference to an `i32` value. We then use the dereference operator to follwo the reference to the value. ```rust fn main() { let x = 5; let y = &x; assert_eq!(5, x); assert_eq!(5, *y); } ``` The variable `x` holds a `5` (`i32`) value. We then set `y` equal to a refernce to `x`. We assert that `x` is equal to `5`. If we want to make an assertion about the value in `y`, we have touse `*y` to follow the reference to the value it's pointing to. Hence the need for a *dereference*. Once we dereference `y` we havce access to the integer value `y` is pointing to so that we can compare with `5` If we tired to use `assert_eq!(5, t);` instead we woul get the compiler error ``` $ cargo run Compiling deref-example v0.1.0 (file:///projects/deref-example) error[E0277]: can't compare `{integer}` with `&{integer}` --> src/main.rs:6:5 | 6 | assert_eq!(5, y); | ^^^^^^^^^^^^^^^^ no implementation for `{integer} == &{integer}` | = help: the trait `PartialEq<&{integer}>` is not implemented for `{integer}` = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider dereferencing here --> file:///home/.rustup/toolchains/1.82/lib/rustlib/src/rust/library/core/src/macros/mod.rs:46:35 | 46| if !(*left_val == **right_val) { | + For more information about this error, try `rustc --explain E0277`. error: could not compile `deref-example` (bin "deref-example") due to 1 previous error ``` Comparing a number to a reference is not allowed because they are differnect types. We must ue the dereference operator to follow the reference to the value they are pointing at. ## Using `Box` Like Reference We can rewrite the code from the example to use a `Box` instead of a reference. Here dereference operator using on the `Box` functions the same way as the dereference operator used on the reference (from before). ```rust fn main() { let x = 5; let y = Box::new(x); assert_eq!(5, x); assert_eq!(5, *y); } ``` The main difference between the two eamples is that we ext `y` to be an instance of a `Box` pointing to a copied value of `x` rather than a reference pointing to the value of `x` In the last assertion we can use the dereference operator to following the pointer of the `Box` in the way as we deferenced `y` when it was a reference. ## Defining Our Own Smart Pointer Here we will bouild a smart pointer simialr to the `Box` type provided by the std library. This is in order to explain fully how smart pointers behave differently from references by defualt. Then we will look into how to add the ability to use the dereference operator. The `Box` type is defined as a tuple struct with one element. In this example we defined `MyBox` type in the same way. We will aslo define a `new` function to match the `new` function defined in `Box` ```rust struct MyBox(T); impl MyBox { fn new(x: T) -> MyBox { MyBox(x) } } ``` Here we defined a struct named `MyBox` and declarce a generic paramter `T` beucause we want our type to hold values of any type. The `MyBox` type is a tuple struct with one element of type `T`. The `MyBox::new` function takes one parameter of type `T` and returns a `MyBox` instance that holds the value passed in. If we try adding the `main` function from above to our preivous examples. We will change it to use `MyBox` type we defined instead of `Box`. The code will not compie because Rust doesn't know how to dereference `MyBox` ```rust fn main() { let x = 5; let y = MyBox::new(x); assert_eq!(5, x); assert_eq!(5, *y); } ``` Here is the resulting compilation error ``` $ cargo run Compiling deref-example v0.1.0 (file:///projects/deref-example) error[E0614]: type `MyBox<{integer}>` cannot be dereferenced --> src/main.rs:14:19 | 14 | assert_eq!(5, *y); | ^^ For more information about this error, try `rustc --explain E0614`. error: could not compile `deref-example` (bin "deref-example") due to 1 previous error ``` The `MyBox` type cant be dereferenced because we havn't implemented the ability to on our type. To enable dereferencing with the `*` operator, we need to implemented the `Deref` trait. ## Treating a Type Like a Reference by Implementing the `Deref` Trait As discussed before to implement a trait we need to provide implementations for the trait's required methods. Here the `Deref` trait, provided by the std library, requires us to implement one method named `deref` that borrows `self` and returns a refernce to the inner data. Here is th implementation of the `Deref` to add to the definition of `MyBox` ```rust use std::ops::Deref; impl Deref for MyBox { type Target = T; fn deref(&self) -> &Self::Target { &self.0 } } ``` The `type Target = T;` syntax defines an associated type for the `Deref` trait to use. Associated types are a slightly different way of declaring a generic paramter, we will cover them later in ch20. We fill in the body of the `deref` method with `&self.0` so `deref` returns a reference to the vbalue we want to access with the `*` operator. Recall that using a "Tuple Struct without a Named Filed to Create Different Types" section. We use `.0` accesses the first value in a tuple struct. The `main` function from the example before will now compile and the assertions will pass now with thuis implementation. Without the `Deref` trait, the compiler can only dereference `&` references. The `deref` method gives the compiler the ability to take a value of any type that implements `Deref` and call the `deref` method to get a `&` reference that it knows how to deference. When we write `*y`. Behind the scenes Rust actually ran this code ```rust *(y.deref()) ``` Rust substitues the `*` operator with a call to `deref` method. Then a plain derefence so we dont have to think about whether or not we need to call the `deref` method. This Rust feature lets us write code that functions identically regardless of if we have a regular reference or a type that implements `Deref` The reason that the `deref` method reutnrs a reference to a value, and that the plain dereferncee outside the parentheses in `*(y.deref())` is still necessary, is to do with the ownership system. If the `deref` method retuned the value directly instead of a refernece to the value, the value would be moved out of `self`. We dont want to take ownership of the inner value insid `MyBox` in this case or in most cases where we use the dereference oerator. Note: the `*` operator is replaced with a call to the `deref` method and then a call to trhe `*` operator just once. Each time we use a `*` in our code. Due to the substituation of the `*` operator doesn't recurse infinitely we end up with data of type `i32`. This then matches the `5` in `assert_eq!` ## Implicit Deref Coercions iwth Functions and Methods *Deref coercion* converts a reference to a type that implements the `Deref` trait into a reference to another type. 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` 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` 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` value. Due to us implementing the `Deref` trait on `MyBox`, Rust can turn `&MyBox` 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` ```rust fn main() { let m = MyBox::new(String::from("Rust")); hello(&(*m)[..]); } ``` The `(*m)` dereferences the `MyBox` 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` - From `&mut T` to `&mut U` when `T: DerefMut` - From `&mut T` to `&U` when `T: Deref` 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.