]> git.proxmox.com Git - rustc.git/blame - src/doc/book/second-edition/src/ch17-02-trait-objects.md
New upstream version 1.28.0~beta.14+dfsg1
[rustc.git] / src / doc / book / second-edition / src / ch17-02-trait-objects.md
CommitLineData
abe05a73
XL
1## Using Trait Objects that Allow for Values of Different Types
2
83c7162d
XL
3In Chapter 8, we mentioned that one limitation of vectors is that they can
4store elements of only one type. We created a workaround in Listing 8-10 where
5we defined a `SpreadsheetCell` enum that had variants to hold integers, floats,
abe05a73
XL
6and text. This meant we could store different types of data in each cell and
7still have a vector that represented a row of cells. This is a perfectly good
8solution when our interchangeable items are a fixed set of types that we know
0531ce1d
XL
9when our code is compiled.
10
11However, sometimes we want our library user to be able to extend the set of
12types that are valid in a particular situation. To show how we might achieve
13this, we’ll create an example graphical user interface (GUI) tool that iterates
14through a list of items, calling a `draw` method on each one to draw it to the
15screen—a common technique for GUI tools. We’ll create a library crate called
16`gui` that contains the structure of a GUI library. This crate might include
17some types for people to use, such as `Button` or `TextField`. In addition,
18`gui` users will want to create their own types that can be drawn: for
19instance, one programmer might add an `Image` and another might add a
20`SelectBox`.
21
22We won’t implement a fully fledged GUI library for this example but will show
abe05a73 23how the pieces would fit together. At the time of writing the library, we can’t
0531ce1d
XL
24know and define all the types other programmers might want to create. But we do
25know that `gui` needs to keep track of many values of different types, and it
26needs to call a `draw` method on each of these differently typed values. It
27doesn’t need to know exactly what will happen when we call the `draw` method,
28just that the value will have that method available for us to call.
abe05a73
XL
29
30To do this in a language with inheritance, we might define a class named
0531ce1d
XL
31`Component` that has a method named `draw` on it. The other classes, such as
32`Button`, `Image`, and `SelectBox`, would inherit from `Component` and thus
abe05a73
XL
33inherit the `draw` method. They could each override the `draw` method to define
34their custom behavior, but the framework could treat all of the types as if
0531ce1d
XL
35they were `Component` instances and call `draw` on them. But because Rust
36doesn’t have inheritance, we need another way to structure the `gui` library to
37allow users to extend it with new types.
abe05a73
XL
38
39### Defining a Trait for Common Behavior
40
0531ce1d
XL
41To implement the behavior we want `gui` to have, we’ll define a trait named
42`Draw` that will have one method named `draw`. Then we can define a vector that
43takes a *trait object*. A trait object points to an instance of a type that
44implements the trait we specify. We create a trait object by specifying some
45sort of pointer, such as a `&` reference or a `Box<T>` smart pointer, and then
83c7162d
XL
46specifying the relevant trait. (We’ll talk about the reason trait objects must
47use a pointer in Chapter 19 in the section “Dynamically Sized Types & Sized”.)
0531ce1d
XL
48We can use trait objects in place of a generic or concrete type. Wherever we
49use a trait object, Rust’s type system will ensure at compile time that any
50value used in that context will implement the trait object’s trait.
51Consequently, we don’t need to know all the possible types at compile time.
abe05a73 52
83c7162d 53We’ve mentioned that in Rust, we refrain from calling structs and enums
abe05a73 54“objects” to distinguish them from other languages’ objects. In a struct or
0531ce1d 55enum, the data in the struct fields and the behavior in `impl` blocks are
83c7162d 56separated, whereas in other languages, the data and behavior combined into one
0531ce1d
XL
57concept is often labeled an object. However, trait objects *are* more like
58objects in other languages in the sense that they combine data and behavior.
59But trait objects differ from traditional objects in that we can’t add data to
60a trait object. Trait objects aren’t as generally useful as objects in other
61languages: their specific purpose is to allow abstraction across common
62behavior.
abe05a73
XL
63
64Listing 17-3 shows how to define a trait named `Draw` with one method named
65`draw`:
cc61c64b
XL
66
67<span class="filename">Filename: src/lib.rs</span>
68
69```rust
70pub trait Draw {
71 fn draw(&self);
72}
73```
74
75<span class="caption">Listing 17-3: Definition of the `Draw` trait</span>
76
0531ce1d
XL
77This syntax should look familiar from our discussions on how to define traits
78in Chapter 10. Next comes some new syntax: Listing 17-4 defines a struct named
abe05a73 79`Screen` that holds a vector named `components`. This vector is of type
83c7162d 80`Box<Draw>`, which is a trait object; it’s a stand-in for any type inside a
abe05a73 81`Box` that implements the `Draw` trait.
cc61c64b 82
cc61c64b
XL
83<span class="filename">Filename: src/lib.rs</span>
84
85```rust
86# pub trait Draw {
87# fn draw(&self);
88# }
89#
90pub struct Screen {
91 pub components: Vec<Box<Draw>>,
92}
93```
94
95<span class="caption">Listing 17-4: Definition of the `Screen` struct with a
abe05a73
XL
96`components` field holding a vector of trait objects that implement the `Draw`
97trait</span>
cc61c64b 98
abe05a73
XL
99On the `Screen` struct, we’ll define a method named `run` that will call the
100`draw` method on each of its `components`, as shown in Listing 17-5:
cc61c64b
XL
101
102<span class="filename">Filename: src/lib.rs</span>
103
104```rust
105# pub trait Draw {
106# fn draw(&self);
107# }
108#
109# pub struct Screen {
110# pub components: Vec<Box<Draw>>,
111# }
112#
113impl Screen {
114 pub fn run(&self) {
115 for component in self.components.iter() {
116 component.draw();
117 }
118 }
119}
120```
121
83c7162d
XL
122<span class="caption">Listing 17-5: A `run` method on `Screen` that calls the
123`draw` method on each component</span>
cc61c64b 124
0531ce1d
XL
125This works differently than defining a struct that uses a generic type
126parameter with trait bounds. A generic type parameter can only be substituted
127with one concrete type at a time, whereas trait objects allow for multiple
128concrete types to fill in for the trait object at runtime. For example, we
129could have defined the `Screen` struct using a generic type and a trait bound
130as in Listing 17-6:
cc61c64b
XL
131
132<span class="filename">Filename: src/lib.rs</span>
133
134```rust
135# pub trait Draw {
136# fn draw(&self);
137# }
138#
139pub struct Screen<T: Draw> {
140 pub components: Vec<T>,
141}
142
143impl<T> Screen<T>
144 where T: Draw {
145 pub fn run(&self) {
146 for component in self.components.iter() {
147 component.draw();
148 }
149 }
150}
151```
152
153<span class="caption">Listing 17-6: An alternate implementation of the `Screen`
154struct and its `run` method using generics and trait bounds</span>
155
abe05a73
XL
156This restricts us to a `Screen` instance that has a list of components all of
157type `Button` or all of type `TextField`. If you’ll only ever have homogeneous
0531ce1d 158collections, using generics and trait bounds is preferable because the
abe05a73 159definitions will be monomorphized at compile time to use the concrete types.
cc61c64b 160
0531ce1d 161On the other hand, with the method using trait objects, one `Screen` instance
94b46f34
XL
162can hold a `Vec<T>` that contains a `Box<Button>` as well as a
163`Box<TextField>`. Let’s look at how this works, and then we’ll talk about the
164runtime performance implications.
cc61c64b 165
abe05a73 166### Implementing the Trait
cc61c64b 167
0531ce1d
XL
168Now we’ll add some types that implement the `Draw` trait. We’ll provide the
169`Button` type. Again, actually implementing a GUI library is beyond the scope
170of this book, so the `draw` method won’t have any useful implementation in its
171body. To imagine what the implementation might look like, a `Button` struct
172might have fields for `width`, `height`, and `label`, as shown in Listing 17-7:
cc61c64b
XL
173
174<span class="filename">Filename: src/lib.rs</span>
175
176```rust
177# pub trait Draw {
178# fn draw(&self);
179# }
180#
181pub struct Button {
182 pub width: u32,
183 pub height: u32,
184 pub label: String,
185}
186
187impl Draw for Button {
188 fn draw(&self) {
83c7162d 189 // code to actually draw a button
cc61c64b
XL
190 }
191}
192```
193
194<span class="caption">Listing 17-7: A `Button` struct that implements the
195`Draw` trait</span>
196
abe05a73 197The `width`, `height`, and `label` fields on `Button` will differ from the
0531ce1d
XL
198fields on other components, such as a `TextField` type, that might have those
199fields plus a `placeholder` field instead. Each of the types we want to draw on
200the screen will implement the `Draw` trait but will use different code in the
83c7162d
XL
201`draw` method to define how to draw that particular type, as `Button` has here
202(without the actual GUI code, which is beyond the scope of this chapter). The
203`Button` type, for instance, might have an additional `impl` block containing
0531ce1d
XL
204methods related to what happens when a user clicks the button. These kinds of
205methods won’t apply to types like `TextField`.
206
207If someone using our library decides to implement a `SelectBox` struct that has
208`width`, `height`, and `options` fields, they implement the `Draw` trait on the
209`SelectBox` type as well, as shown in Listing 17-8:
cc61c64b
XL
210
211<span class="filename">Filename: src/main.rs</span>
212
213```rust,ignore
0531ce1d
XL
214extern crate gui;
215use gui::Draw;
cc61c64b
XL
216
217struct SelectBox {
218 width: u32,
219 height: u32,
220 options: Vec<String>,
221}
222
223impl Draw for SelectBox {
224 fn draw(&self) {
83c7162d 225 // code to actually draw a select box
cc61c64b
XL
226 }
227}
228```
229
0531ce1d
XL
230<span class="caption">Listing 17-8: Another crate using `gui` and implementing
231the `Draw` trait on a `SelectBox` struct</span>
cc61c64b 232
0531ce1d
XL
233Our library’s user can now write their `main` function to create a `Screen`
234instance. To the `Screen` instance, they can add a `SelectBox` and a `Button`
235by putting each in a `Box<T>` to become a trait object. They can then call the
236`run` method on the `Screen` instance, which will call `draw` on each of the
cc61c64b
XL
237components. Listing 17-9 shows this implementation:
238
239<span class="filename">Filename: src/main.rs</span>
240
241```rust,ignore
0531ce1d 242use gui::{Screen, Button};
cc61c64b
XL
243
244fn main() {
245 let screen = Screen {
246 components: vec![
247 Box::new(SelectBox {
248 width: 75,
249 height: 10,
250 options: vec![
251 String::from("Yes"),
252 String::from("Maybe"),
253 String::from("No")
254 ],
255 }),
256 Box::new(Button {
257 width: 50,
258 height: 10,
259 label: String::from("OK"),
260 }),
261 ],
262 };
263
264 screen.run();
265}
266```
267
268<span class="caption">Listing 17-9: Using trait objects to store values of
269different types that implement the same trait</span>
270
0531ce1d
XL
271When we wrote the library, we didn’t know that someone might add the
272`SelectBox` type, but our `Screen` implementation was able to operate on the
273new type and draw it because `SelectBox` implements the `Draw` type, which
274means it implements the `draw` method.
275
276This concept—of being concerned only with the messages a value responds to
277rather than the value’s concrete type—is similar to the concept *duck typing*
278in dynamically typed languages: if it walks like a duck and quacks like a duck,
279then it must be a duck! In the implementation of `run` on `Screen` in Listing
28017-5, `run` doesn’t need to know what the concrete type of each component is.
281It doesn’t check whether a component is an instance of a `Button` or a
282`SelectBox`, it just calls the `draw` method on the component. By specifying
283`Box<Draw>` as the type of the values in the `components` vector, we’ve defined
284`Screen` to need values that we can call the `draw` method on.
285
286The advantage of using trait objects and Rust’s type system to write code
83c7162d
XL
287similar to code using duck typing is that we never have to check whether a
288value implements a particular method at runtime or worry about getting errors
289if a value doesn’t implement a method but we call it anyway. Rust won’t compile
290our code if the values don’t implement the traits that the trait objects need.
cc61c64b
XL
291
292For example, Listing 17-10 shows what happens if we try to create a `Screen`
293with a `String` as a component:
294
295<span class="filename">Filename: src/main.rs</span>
296
297```rust,ignore
0531ce1d
XL
298extern crate gui;
299use gui::Screen;
cc61c64b
XL
300
301fn main() {
302 let screen = Screen {
303 components: vec![
304 Box::new(String::from("Hi")),
305 ],
306 };
307
308 screen.run();
309}
310```
311
3b2f2976
XL
312<span class="caption">Listing 17-10: Attempting to use a type that doesn’t
313implement the trait object’s trait</span>
cc61c64b 314
0531ce1d 315We’ll get this error because `String` doesn’t implement the `Draw` trait:
cc61c64b
XL
316
317```text
0531ce1d
XL
318error[E0277]: the trait bound `std::string::String: gui::Draw` is not satisfied
319 --> src/main.rs:7:13
cc61c64b 320 |
0531ce1d
XL
321 7 | Box::new(String::from("Hi")),
322 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait gui::Draw is not
cc61c64b
XL
323 implemented for `std::string::String`
324 |
0531ce1d 325 = note: required for the cast to the object type `gui::Draw`
cc61c64b
XL
326```
327
0531ce1d 328This error lets us know that either we’re passing something to `Screen` we
83c7162d 329didn’t mean to pass and we should pass a different type or we should implement
0531ce1d 330`Draw` on `String` so that `Screen` is able to call `draw` on it.
cc61c64b
XL
331
332### Trait Objects Perform Dynamic Dispatch
333
0531ce1d
XL
334Recall in the “Performance of Code Using Generics” section in Chapter 10 our
335discussion on the monomorphization process performed by the compiler when we
83c7162d
XL
336use trait bounds on generics: the compiler generates nongeneric implementations
337of functions and methods for each concrete type that we use in place of a
338generic type parameter. The code that results from monomorphization is doing
339*static dispatch*, which is when the compiler knows what method you’re calling
340at compile time. This is opposed to *dynamic dispatch*, which is when the
341compiler can’t tell at compile time which method you’re calling. In dynamic
0531ce1d
XL
342dispatch cases, the compiler emits code that at runtime will figure out which
343method to call.
344
345When we use trait objects, Rust must use dynamic dispatch. The compiler doesn’t
346know all the types that might be used with the code that is using trait
abe05a73 347objects, so it doesn’t know which method implemented on which type to call.
0531ce1d 348Instead, at runtime, Rust uses the pointers inside the trait object to know
83c7162d
XL
349which method to call. There is a runtime cost when this lookup happens that
350doesn’t occur with static dispatch. Dynamic dispatch also prevents the compiler
351from choosing to inline a method’s code, which in turn prevents some
0531ce1d
XL
352optimizations. However, we did get extra flexibility in the code that we wrote
353in Listing 17-5 and were able to support in Listing 17-9, so it’s a trade-off
354to consider.
355
356### Object Safety Is Required for Trait Objects
357
83c7162d 358You can only make *object-safe* traits into trait objects. Some complex rules
0531ce1d
XL
359govern all the properties that make a trait object safe, but in practice, only
360two rules are relevant. A trait is object safe if all the methods defined in
361the trait have the following properties:
362
363* The return type isn’t `Self`.
364* There are no generic type parameters.
365
366The `Self` keyword is an alias for the type we’re implementing the traits or
367methods on. Trait objects must be object safe because once you’ve used a trait
368object, Rust no longer knows the concrete type that’s implementing that trait.
369If a trait method returns the concrete `Self` type, but a trait object forgets
370the exact type that `Self` is, there is no way the method can use the original
371concrete type. The same is true of generic type parameters that are filled in
372with concrete type parameters when the trait is used: the concrete types become
373part of the type that implements the trait. When the type is forgotten through
374the use of a trait object, there is no way to know what types to fill in the
375generic type parameters with.
cc61c64b
XL
376
377An example of a trait whose methods are not object safe is the standard
3b2f2976 378library’s `Clone` trait. The signature for the `clone` method in the `Clone`
cc61c64b
XL
379trait looks like this:
380
381```rust
382pub trait Clone {
383 fn clone(&self) -> Self;
384}
385```
386
0531ce1d
XL
387The `String` type implements the `Clone` trait, and when we call the `clone`
388method on an instance of `String` we get back an instance of `String`.
94b46f34
XL
389Similarly, if we call `clone` on an instance of `Vec<T>`, we get back an
390instance of `Vec<T>`. The signature of `clone` needs to know what type will
391stand in for `Self`, because that’s the return type.
cc61c64b 392
0531ce1d 393The compiler will indicate when you’re trying to do something that violates the
83c7162d 394rules of object safety in regard to trait objects. For example, let’s say we
cc61c64b
XL
395tried to implement the `Screen` struct in Listing 17-4 to hold types that
396implement the `Clone` trait instead of the `Draw` trait, like this:
397
398```rust,ignore
399pub struct Screen {
400 pub components: Vec<Box<Clone>>,
401}
402```
403
0531ce1d 404We would get this error:
cc61c64b
XL
405
406```text
407error[E0038]: the trait `std::clone::Clone` cannot be made into an object
0531ce1d 408 --> src/lib.rs:2:5
cc61c64b
XL
409 |
4102 | pub components: Vec<Box<Clone>>,
411 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::clone::Clone` cannot be
0531ce1d 412made into an object
cc61c64b
XL
413 |
414 = note: the trait cannot require that `Self : Sized`
415```
416
0531ce1d
XL
417This error means you can’t use this trait as a trait object in this way. If
418you’re interested in more details on object safety, see [Rust RFC 255].
abe05a73
XL
419
420[Rust RFC 255]: https://github.com/rust-lang/rfcs/blob/master/text/0255-object-safety.md