]>
Commit | Line | Data |
---|---|---|
cc61c64b XL |
1 | ## The `Deref` Trait Allows Access to the Data Through a Reference |
2 | ||
3 | The first important smart pointer-related trait is `Deref`, which allows us to | |
4 | override `*`, the dereference operator (as opposed to the multiplication | |
5 | operator or the glob operator). Overriding `*` for smart pointers makes | |
3b2f2976 | 6 | accessing the data behind the smart pointer convenient, and we’ll talk about |
cc61c64b XL |
7 | what we mean by convenient when we get to deref coercions later in this section. |
8 | ||
9 | We briefly mentioned the dereference operator in Chapter 8, in the hash map | |
3b2f2976 | 10 | section titled “Update a Value Based on the Old Value”. We had a mutable |
cc61c64b | 11 | reference, and we wanted to change the value that the reference was pointing |
3b2f2976 | 12 | to. In order to do that, first we had to dereference the reference. Here’s |
cc61c64b XL |
13 | another example using references to `i32` values: |
14 | ||
15 | ```rust | |
16 | let mut x = 5; | |
17 | { | |
18 | let y = &mut x; | |
19 | ||
20 | *y += 1 | |
21 | } | |
22 | ||
23 | assert_eq!(6, x); | |
24 | ``` | |
25 | ||
26 | We use `*y` to access the data that the mutable reference in `y` refers to, | |
27 | rather than the mutable reference itself. We can then modify that data, in this | |
28 | case by adding 1. | |
29 | ||
3b2f2976 | 30 | With references that aren’t smart pointers, there’s only one value that the |
cc61c64b XL |
31 | reference is pointing to, so the dereference operation is straightforward. |
32 | Smart pointers can also store metadata about the pointer or the data. When | |
33 | dereferencing a smart pointer, we only want the data, not the metadata, since | |
34 | dereferencing a regular reference only gives us data and not metadata. We want | |
35 | to be able to use smart pointers in the same places that we can use regular | |
36 | references. To enable that, we can override the behavior of the `*` operator by | |
37 | implementing the `Deref` trait. | |
38 | ||
3b2f2976 | 39 | Listing 15-7 has an example of overriding `*` using `Deref` on a struct we’ve |
cc61c64b XL |
40 | defined to hold mp3 data and metadata. `Mp3` is, in a sense, a smart pointer: |
41 | it owns the `Vec<u8>` data containing the audio. In addition, it holds some | |
42 | optional metadata, in this case the artist and title of the song in the audio | |
43 | data. We want to be able to conveniently access the audio data, not the | |
44 | metadata, so we implement the `Deref` trait to return the audio data. | |
45 | Implementing the `Deref` trait requires implementing one method named `deref` | |
46 | that borrows `self` and returns the inner data: | |
47 | ||
48 | <span class="filename">Filename: src/main.rs</span> | |
49 | ||
50 | ```rust | |
51 | use std::ops::Deref; | |
52 | ||
53 | struct Mp3 { | |
54 | audio: Vec<u8>, | |
55 | artist: Option<String>, | |
56 | title: Option<String>, | |
57 | } | |
58 | ||
59 | impl Deref for Mp3 { | |
60 | type Target = Vec<u8>; | |
61 | ||
62 | fn deref(&self) -> &Vec<u8> { | |
63 | &self.audio | |
64 | } | |
65 | } | |
66 | ||
67 | fn main() { | |
68 | let my_favorite_song = Mp3 { | |
69 | // we would read the actual audio data from an mp3 file | |
70 | audio: vec![1, 2, 3], | |
71 | artist: Some(String::from("Nirvana")), | |
72 | title: Some(String::from("Smells Like Teen Spirit")), | |
73 | }; | |
74 | ||
75 | assert_eq!(vec![1, 2, 3], *my_favorite_song); | |
76 | } | |
77 | ``` | |
78 | ||
79 | <span class="caption">Listing 15-7: An implementation of the `Deref` trait on a | |
80 | struct that holds mp3 file data and metadata</span> | |
81 | ||
82 | Most of this should look familiar: a struct, a trait implementation, and a | |
83 | main function that creates an instance of the struct. There is one part we | |
3b2f2976 | 84 | haven’t explained thoroughly yet: similarly to Chapter 13 when we looked at the |
cc61c64b | 85 | Iterator trait with the `type Item`, the `type Target = T;` syntax is defining |
3b2f2976 | 86 | an associated type, which is covered in more detail in Chapter 19. Don’t worry |
cc61c64b XL |
87 | about that part of the example too much; it is a slightly different way of |
88 | declaring a generic parameter. | |
89 | ||
3b2f2976 | 90 | In the `assert_eq!`, we’re verifying that `vec![1, 2, 3]` is the result we get |
cc61c64b XL |
91 | when dereferencing the `Mp3` instance with `*my_favorite_song`, which is what |
92 | happens since we implemented the `deref` method to return the audio data. If | |
3b2f2976 XL |
93 | we hadn’t implemented the `Deref` trait for `Mp3`, Rust wouldn’t compile the |
94 | code `*my_favorite_song`: we’d get an error saying type `Mp3` cannot be | |
cc61c64b XL |
95 | dereferenced. |
96 | ||
97 | Without the `Deref` trait, the compiler can only dereference `&` references, | |
98 | which `my_favorite_song` is not (it is an `Mp3` struct). With the `Deref` | |
99 | trait, the compiler knows that types implementing the `Deref` trait have a | |
100 | `deref` method that returns a reference (in this case, `&self.audio` because of | |
101 | our definition of `deref` in Listing 15-7). So in order to get a `&` reference | |
102 | that `*` can dereference, the compiler expands `*my_favorite_song` to this: | |
103 | ||
104 | ```rust,ignore | |
105 | *(my_favorite_song.deref()) | |
106 | ``` | |
107 | ||
108 | The result is the value in `self.audio`. The reason `deref` returns a reference | |
109 | that we then have to dereference, rather than just returning a value directly, | |
110 | is because of ownership: if the `deref` method directly returned the value | |
3b2f2976 | 111 | instead of a reference to it, the value would be moved out of `self`. We don’t |
cc61c64b XL |
112 | want to take ownership of `my_favorite_song.audio` in this case and most cases |
113 | where we use the dereference operator. | |
114 | ||
115 | Note that replacing `*` with a call to the `deref` method and then a call to | |
116 | `*` happens once, each time the `*` is used. The substitution of `*` does not | |
3b2f2976 | 117 | recurse infinitely. That’s how we end up with data of type `Vec<u8>`, which |
cc61c64b XL |
118 | matches the `vec![1, 2, 3]` in the `assert_eq!` in Listing 15-7. |
119 | ||
120 | ### Implicit Deref Coercions with Functions and Methods | |
121 | ||
122 | Rust tends to favor explicitness over implicitness, but one case where this | |
123 | does not hold true is *deref coercions* of arguments to functions and methods. | |
124 | A deref coercion will automatically convert a reference to any pointer into a | |
3b2f2976 | 125 | reference to that pointer’s contents. A deref coercion happens when the |
cc61c64b | 126 | reference type of the argument passed into the function differs from the |
3b2f2976 | 127 | reference type of the parameter defined in that function’s signature. Deref |
cc61c64b XL |
128 | coercion was added to Rust to make calling functions and methods not need as |
129 | many explicit references and dereferences with `&` and `*`. | |
130 | ||
3b2f2976 | 131 | Using our `Mp3` struct from Listing 15-7, here’s the signature of a function to |
cc61c64b XL |
132 | compress mp3 audio data that takes a slice of `u8`: |
133 | ||
134 | ```rust,ignore | |
135 | fn compress_mp3(audio: &[u8]) -> Vec<u8> { | |
136 | // the actual implementation would go here | |
137 | } | |
138 | ``` | |
139 | ||
3b2f2976 XL |
140 | If Rust didn’t have deref coercion, in order to call this function with the |
141 | audio data in `my_favorite_song`, we’d have to write: | |
cc61c64b XL |
142 | |
143 | ```rust,ignore | |
144 | compress_mp3(my_favorite_song.audio.as_slice()) | |
145 | ``` | |
146 | ||
3b2f2976 | 147 | That is, we’d have to explicitly say that we want the data in the `audio` field |
cc61c64b | 148 | of `my_favorite_song` and that we want a slice referring to the whole |
3b2f2976 | 149 | `Vec<u8>`. If there were a lot of places where we’d want to process the `audio` |
cc61c64b XL |
150 | data in a similar manner, `.audio.as_slice()` would be wordy and repetitive. |
151 | ||
152 | However, because of deref coercion and our implementation of the `Deref` trait | |
153 | on `Mp3`, we can call this function with the data in `my_favorite_song` by | |
154 | using this code: | |
155 | ||
156 | ```rust,ignore | |
157 | let result = compress_mp3(&my_favorite_song); | |
158 | ``` | |
159 | ||
160 | Just an `&` and the instance, nice! We can treat our smart pointer as if it was | |
161 | a regular reference. Deref coercion means that Rust can use its knowledge of | |
162 | our `Deref` implementation, namely: Rust knows that `Mp3` implements the | |
163 | `Deref` trait and returns `&Vec<u8>` from the `deref` method. Rust also knows | |
164 | the standard library implements the `Deref` trait on `Vec<T>` to return `&[T]` | |
165 | from the `deref` method (and we can find that out too by looking at the API | |
166 | documentation for `Vec<T>`). So, at compile time, Rust will see that it can use | |
167 | `Deref::deref` twice to turn `&Mp3` into `&Vec<u8>` and then into `&[T]` to | |
168 | match the signature of `compress_mp3`. That means we get to do less typing! | |
169 | Rust will analyze types through `Deref::deref` as many times as it needs to in | |
3b2f2976 | 170 | order to get a reference to match the parameter’s type, when the `Deref` trait |
cc61c64b XL |
171 | is defined for the types involved. This indirection is resolved at compile time, |
172 | so there is no run-time penalty for taking advantage of deref coercion! | |
173 | ||
174 | Similar to how we use the `Deref` trait to override `*` on `&T`s, there is also | |
175 | a `DerefMut` trait for overriding `*` on `&mut T`. | |
176 | ||
177 | Rust does deref coercion when it finds types and trait implementations in three | |
178 | cases: | |
179 | ||
180 | * From `&T` to `&U` when `T: Deref<Target=U>`. | |
181 | * From `&mut T` to `&mut U` when `T: DerefMut<Target=U>`. | |
182 | * From `&mut T` to `&U` when `T: Deref<Target=U>`. | |
183 | ||
184 | The first two are the same, except for mutability: if you have a `&T`, and | |
185 | `T` implements `Deref` to some type `U`, you can get a `&U` transparently. Same | |
186 | for mutable references. The last one is more tricky: if you have a mutable | |
187 | reference, it will also coerce to an immutable one. The other case is _not_ | |
188 | possible though: immutable references will never coerce to mutable ones. | |
189 | ||
190 | The reason that the `Deref` trait is important to the smart pointer pattern is | |
191 | that smart pointers can then be treated like regular references and used in | |
3b2f2976 | 192 | places that expect regular references. We don’t have to redefine methods and |
cc61c64b | 193 | functions to take smart pointers explicitly, for example. |