mirror of
https://github.com/darkicewolf50/RustBrock.git
synced 2025-06-15 13:04:18 -06:00
started ch15.2
This commit is contained in:
parent
ad92fedb9c
commit
856e0e992a
224
Deref Trait.md
224
Deref Trait.md
@ -1 +1,223 @@
|
||||
# Treating Smart Pointers Like Regular References with the `Deref` Trait
|
||||
# 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<T>`.
|
||||
|
||||
Then we will see why the dereference operator doesnt work like a reference on our newly behaves lik `Box<T>`.
|
||||
|
||||
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<T>` type, the custom type we are about to build and the real `Box<T>`.
|
||||
|
||||
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<T>` Like Reference
|
||||
We can rewrite the code from the example to use a `Box<T>` instead of a reference.
|
||||
|
||||
Here dereference operator using on the `Box<T>` 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<T>` 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<T>` 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<T>` 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<T>` type is defined as a tuple struct with one element.
|
||||
|
||||
In this example we defined `MyBox<T>` type in the same way.
|
||||
|
||||
We will aslo define a `new` function to match the `new` function defined in `Box<T>`
|
||||
```rust
|
||||
struct MyBox<T>(T);
|
||||
|
||||
impl<T> MyBox<T> {
|
||||
fn new(x: T) -> MyBox<T> {
|
||||
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<T>` type we defined instead of `Box<T>`.
|
||||
|
||||
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<T>` 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<T> Deref for MyBox<T> {
|
||||
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<T>` 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`.
|
||||
|
||||
|
@ -1 +1 @@
|
||||
{"rustc_fingerprint":4305361489769004817,"outputs":{"17747080675513052775":{"success":true,"status":"","code":0,"stdout":"rustc 1.85.0 (4d91de4e4 2025-02-17)\nbinary: rustc\ncommit-hash: 4d91de4e48198da2e33413efdcd9cd2cc0c46688\ncommit-date: 2025-02-17\nhost: x86_64-unknown-linux-gnu\nrelease: 1.85.0\nLLVM version: 19.1.7\n","stderr":""},"13331785392996375709":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/brock/.rustup/toolchains/stable-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""}},"successes":{}}
|
||||
{"rustc_fingerprint":4305361489769004817,"outputs":{"17747080675513052775":{"success":true,"status":"","code":0,"stdout":"rustc 1.85.0 (4d91de4e4 2025-02-17)\nbinary: rustc\ncommit-hash: 4d91de4e48198da2e33413efdcd9cd2cc0c46688\ncommit-date: 2025-02-17\nhost: x86_64-unknown-linux-gnu\nrelease: 1.85.0\nLLVM version: 19.1.7\n","stderr":""},"2063776225603076451":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/brock/.rustup/toolchains/stable-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""},"13331785392996375709":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/brock/.rustup/toolchains/stable-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""}},"successes":{}}
|
@ -1 +1 @@
|
||||
{"rustc_fingerprint":4305361489769004817,"outputs":{"17747080675513052775":{"success":true,"status":"","code":0,"stdout":"rustc 1.85.0 (4d91de4e4 2025-02-17)\nbinary: rustc\ncommit-hash: 4d91de4e48198da2e33413efdcd9cd2cc0c46688\ncommit-date: 2025-02-17\nhost: x86_64-unknown-linux-gnu\nrelease: 1.85.0\nLLVM version: 19.1.7\n","stderr":""},"13331785392996375709":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/brock/.rustup/toolchains/stable-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""}},"successes":{}}
|
||||
{"rustc_fingerprint":4305361489769004817,"outputs":{"2063776225603076451":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/brock/.rustup/toolchains/stable-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""},"13331785392996375709":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/brock/.rustup/toolchains/stable-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""},"17747080675513052775":{"success":true,"status":"","code":0,"stdout":"rustc 1.85.0 (4d91de4e4 2025-02-17)\nbinary: rustc\ncommit-hash: 4d91de4e48198da2e33413efdcd9cd2cc0c46688\ncommit-date: 2025-02-17\nhost: x86_64-unknown-linux-gnu\nrelease: 1.85.0\nLLVM version: 19.1.7\n","stderr":""}},"successes":{}}
|
Loading…
x
Reference in New Issue
Block a user