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