]> git.proxmox.com Git - rustc.git/blame - src/doc/book/nostarch/chapter17.md
New upstream version 1.63.0+dfsg1
[rustc.git] / src / doc / book / nostarch / chapter17.md
CommitLineData
5099ac24
FG
1<!-- DO NOT EDIT THIS FILE.
2
3This file is periodically generated from the content in the `/src/`
4directory, so all fixes need to be made in `/src/`.
5-->
6
7[TOC]
8
923072b8
FG
9# Object-Oriented Programming Features of Rust
10
11Object-oriented programming (OOP) is a way of modeling programs. Objects as a
12programmatic concept were introduced in the programming language Simula in the
131960s. Those objects influenced Alan Kay’s programming architecture in which
14objects pass messages to each other. To describe this architecture, he coined
15the term *object-oriented programming* in 1967. Many competing definitions
16describe what OOP is, and by some of these definitions Rust is object-oriented,
17but by others it is not. In this chapter, we’ll explore certain characteristics
18that are commonly considered object-oriented and how those characteristics
19translate to idiomatic Rust. We’ll then show you how to implement an
20object-oriented design pattern in Rust and discuss the trade-offs of doing so
21versus implementing a solution using some of Rust’s strengths instead.
22
23<!-- Nit: we should probably use "object-oriented" throughout, rather using both
24"object-oriented" and "object oriented"
25/JT -->
26<!-- Done! /Carol -->
5099ac24
FG
27
28## Characteristics of Object-Oriented Languages
29
30There is no consensus in the programming community about what features a
923072b8 31language must have to be considered object-oriented. Rust is influenced by many
5099ac24
FG
32programming paradigms, including OOP; for example, we explored the features
33that came from functional programming in Chapter 13. Arguably, OOP languages
34share certain common characteristics, namely objects, encapsulation, and
35inheritance. Let’s look at what each of those characteristics means and whether
36Rust supports it.
37
38### Objects Contain Data and Behavior
39
40The book *Design Patterns: Elements of Reusable Object-Oriented Software* by
41Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides (Addison-Wesley
42Professional, 1994), colloquially referred to as *The Gang of Four* book, is a
43catalog of object-oriented design patterns. It defines OOP this way:
44
45> Object-oriented programs are made up of objects. An *object* packages both
46> data and the procedures that operate on that data. The procedures are
47> typically called *methods* or *operations*.
48
923072b8 49Using this definition, Rust is object-oriented: structs and enums have data,
5099ac24
FG
50and `impl` blocks provide methods on structs and enums. Even though structs and
51enums with methods aren’t *called* objects, they provide the same
52functionality, according to the Gang of Four’s definition of objects.
53
54### Encapsulation that Hides Implementation Details
55
56Another aspect commonly associated with OOP is the idea of *encapsulation*,
57which means that the implementation details of an object aren’t accessible to
58code using that object. Therefore, the only way to interact with an object is
59through its public API; code using the object shouldn’t be able to reach into
60the object’s internals and change data or behavior directly. This enables the
61programmer to change and refactor an object’s internals without needing to
62change the code that uses the object.
63
64We discussed how to control encapsulation in Chapter 7: we can use the `pub`
65keyword to decide which modules, types, functions, and methods in our code
66should be public, and by default everything else is private. For example, we
67can define a struct `AveragedCollection` that has a field containing a vector
68of `i32` values. The struct can also have a field that contains the average of
69the values in the vector, meaning the average doesn’t have to be computed
70on demand whenever anyone needs it. In other words, `AveragedCollection` will
71cache the calculated average for us. Listing 17-1 has the definition of the
72`AveragedCollection` struct:
73
74Filename: src/lib.rs
75
76```
77pub struct AveragedCollection {
78 list: Vec<i32>,
79 average: f64,
80}
81```
82
83Listing 17-1: An `AveragedCollection` struct that maintains a list of integers
84and the average of the items in the collection
85
86The struct is marked `pub` so that other code can use it, but the fields within
87the struct remain private. This is important in this case because we want to
88ensure that whenever a value is added or removed from the list, the average is
89also updated. We do this by implementing `add`, `remove`, and `average` methods
90on the struct, as shown in Listing 17-2:
91
92Filename: src/lib.rs
93
94```
95impl AveragedCollection {
96 pub fn add(&mut self, value: i32) {
97 self.list.push(value);
98 self.update_average();
99 }
100
101 pub fn remove(&mut self) -> Option<i32> {
102 let result = self.list.pop();
103 match result {
104 Some(value) => {
105 self.update_average();
106 Some(value)
107 }
108 None => None,
109 }
110 }
111
112 pub fn average(&self) -> f64 {
113 self.average
114 }
115
116 fn update_average(&mut self) {
117 let total: i32 = self.list.iter().sum();
118 self.average = total as f64 / self.list.len() as f64;
119 }
120}
121```
122
923072b8
FG
123<!-- The above example will crash with a division by zero if you call it at
124any time when it's empty. Not sure if we want to fix, but thought I'd point
125it out.
126/JT -->
127<!-- It actually won't because f64 / 0 is NaN, not a panic /Carol -->
128
5099ac24
FG
129Listing 17-2: Implementations of the public methods `add`, `remove`, and
130`average` on `AveragedCollection`
131
132The public methods `add`, `remove`, and `average` are the only ways to access
133or modify data in an instance of `AveragedCollection`. When an item is added
134to `list` using the `add` method or removed using the `remove` method, the
135implementations of each call the private `update_average` method that handles
136updating the `average` field as well.
137
138We leave the `list` and `average` fields private so there is no way for
923072b8
FG
139external code to add or remove items to or from the `list` field directly;
140otherwise, the `average` field might become out of sync when the `list`
141changes. The `average` method returns the value in the `average` field,
142allowing external code to read the `average` but not modify it.
5099ac24
FG
143
144Because we’ve encapsulated the implementation details of the struct
145`AveragedCollection`, we can easily change aspects, such as the data structure,
146in the future. For instance, we could use a `HashSet<i32>` instead of a
147`Vec<i32>` for the `list` field. As long as the signatures of the `add`,
148`remove`, and `average` public methods stay the same, code using
149`AveragedCollection` wouldn’t need to change. If we made `list` public instead,
150this wouldn’t necessarily be the case: `HashSet<i32>` and `Vec<i32>` have
151different methods for adding and removing items, so the external code would
152likely have to change if it were modifying `list` directly.
153
923072b8
FG
154If encapsulation is a required aspect for a language to be considered
155object-oriented, then Rust meets that requirement. The option to use `pub` or
156not for different parts of code enables encapsulation of implementation details.
5099ac24
FG
157
158### Inheritance as a Type System and as Code Sharing
159
923072b8
FG
160*Inheritance* is a mechanism whereby an object can inherit elements from
161another object’s definition, thus gaining the parent object’s data and behavior
162without you having to define them again.
5099ac24
FG
163
164If a language must have inheritance to be an object-oriented language, then
165Rust is not one. There is no way to define a struct that inherits the parent
923072b8
FG
166struct’s fields and method implementations without using a macro.
167
168However, if you’re used to having inheritance in your programming toolbox, you
169can use other solutions in Rust, depending on your reason for reaching for
170inheritance in the first place.
171
172You would choose inheritance for two main reasons. One is for reuse of code:
173you can implement particular behavior for one type, and inheritance enables you
174to reuse that implementation for a different type. You can do this in a limited
175way in Rust code using default trait method implementations, which you saw in
176Listing 10-14 when we added a default implementation of the `summarize` method
177on the `Summary` trait. Any type implementing the `Summary` trait would have
178the `summarize` method available on it without any further code. This is
179similar to a parent class having an implementation of a method and an
180inheriting child class also having the implementation of the method. We can
181also override the default implementation of the `summarize` method when we
182implement the `Summary` trait, which is similar to a child class overriding the
5099ac24
FG
183implementation of a method inherited from a parent class.
184
923072b8
FG
185<!-- I'm a bit uncomfortable with the above. I think it's more honest to say
186that Rust doesn't support inheritance unless you use a macro. Saying to use
187the trait system to an OO programmer is going to leave them pretty confused, as
188traits lack of the basics of inheritance: you can't use and modify state, you
189have to use a surrogate type to hold the trait implementation, you can't
190instantiate, and so on.
191
192The example that came to mind: trying to teach OO programmers who want to
193build a UI library with traditional OO techniques using the trait system.
194It's unfortunately not going to work very well, if at all.
195
196A trait's main focus is polymorphism and not inheritance. It's probably
197better for folks coming from OO backgrounds if we just come out and say it, tbh.
198/JT -->
199<!-- I agree, and I've made some edits to the paragraphs above /Carol -->
200
5099ac24
FG
201The other reason to use inheritance relates to the type system: to enable a
202child type to be used in the same places as the parent type. This is also
203called *polymorphism*, which means that you can substitute multiple objects for
204each other at runtime if they share certain characteristics.
205
206> ### Polymorphism
207>
208> To many people, polymorphism is synonymous with inheritance. But it’s
209> actually a more general concept that refers to code that can work with data
210> of multiple types. For inheritance, those types are generally subclasses.
211>
212> Rust instead uses generics to abstract over different possible types and
213> trait bounds to impose constraints on what those types must provide. This is
214> sometimes called *bounded parametric polymorphism*.
215
216Inheritance has recently fallen out of favor as a programming design solution
217in many programming languages because it’s often at risk of sharing more code
218than necessary. Subclasses shouldn’t always share all characteristics of their
219parent class but will do so with inheritance. This can make a program’s design
220less flexible. It also introduces the possibility of calling methods on
221subclasses that don’t make sense or that cause errors because the methods don’t
923072b8
FG
222apply to the subclass. In addition, some languages will only allow single
223inheritance (meaning a subclass can only inherit from one class), further
224restricting the flexibility of a program’s design.
225
226<!-- Nit - "inherit from one class" and "single-inheritance" read a bit
227differently to me. Saying you inherit from only one class almost makes it sound
228like that the class you inherit from can't have a parent. Probably minor, just
229made me read that sentence a couple times.
230/JT -->
231<!-- I've included the term "single inheritance" above (it appears that usually
232it's not hyphenated) but kept what was there as an explanation in case the
233reader isn't familiar. /Carol -->
234
235For these reasons, Rust takes the different approach of using trait objects
236instead of inheritance. Let’s look at how trait objects enable polymorphism in
237Rust.
5099ac24
FG
238
239## Using Trait Objects That Allow for Values of Different Types
240
241In Chapter 8, we mentioned that one limitation of vectors is that they can
242store elements of only one type. We created a workaround in Listing 8-10 where
243we defined a `SpreadsheetCell` enum that had variants to hold integers, floats,
244and text. This meant we could store different types of data in each cell and
245still have a vector that represented a row of cells. This is a perfectly good
246solution when our interchangeable items are a fixed set of types that we know
247when our code is compiled.
248
249However, sometimes we want our library user to be able to extend the set of
250types that are valid in a particular situation. To show how we might achieve
251this, we’ll create an example graphical user interface (GUI) tool that iterates
252through a list of items, calling a `draw` method on each one to draw it to the
253screen—a common technique for GUI tools. We’ll create a library crate called
254`gui` that contains the structure of a GUI library. This crate might include
255some types for people to use, such as `Button` or `TextField`. In addition,
256`gui` users will want to create their own types that can be drawn: for
257instance, one programmer might add an `Image` and another might add a
258`SelectBox`.
259
260We won’t implement a fully fledged GUI library for this example but will show
261how the pieces would fit together. At the time of writing the library, we can’t
262know and define all the types other programmers might want to create. But we do
263know that `gui` needs to keep track of many values of different types, and it
264needs to call a `draw` method on each of these differently typed values. It
265doesn’t need to know exactly what will happen when we call the `draw` method,
266just that the value will have that method available for us to call.
267
268To do this in a language with inheritance, we might define a class named
269`Component` that has a method named `draw` on it. The other classes, such as
270`Button`, `Image`, and `SelectBox`, would inherit from `Component` and thus
271inherit the `draw` method. They could each override the `draw` method to define
272their custom behavior, but the framework could treat all of the types as if
273they were `Component` instances and call `draw` on them. But because Rust
274doesn’t have inheritance, we need another way to structure the `gui` library to
275allow users to extend it with new types.
276
277### Defining a Trait for Common Behavior
278
279To implement the behavior we want `gui` to have, we’ll define a trait named
280`Draw` that will have one method named `draw`. Then we can define a vector that
281takes a *trait object*. A trait object points to both an instance of a type
923072b8
FG
282implementing our specified trait and a table used to look up trait methods on
283that type at runtime. We create a trait object by specifying some sort of
284pointer, such as a `&` reference or a `Box<T>` smart pointer, then the `dyn`
285keyword, and then specifying the relevant trait. (We’ll talk about the reason
286trait objects must use a pointer in Chapter 19 in the section “Dynamically
287Sized Types and the `Sized` Trait.”) We can use trait objects in place of a
288generic or concrete type. Wherever we use a trait object, Rust’s type system
289will ensure at compile time that any value used in that context will implement
290the trait object’s trait. Consequently, we don’t need to know all the possible
291types at compile time.
292
293We’ve mentioned that, in Rust, we refrain from calling structs and enums
5099ac24
FG
294“objects” to distinguish them from other languages’ objects. In a struct or
295enum, the data in the struct fields and the behavior in `impl` blocks are
296separated, whereas in other languages, the data and behavior combined into one
297concept is often labeled an object. However, trait objects *are* more like
298objects in other languages in the sense that they combine data and behavior.
299But trait objects differ from traditional objects in that we can’t add data to
300a trait object. Trait objects aren’t as generally useful as objects in other
301languages: their specific purpose is to allow abstraction across common
302behavior.
303
304Listing 17-3 shows how to define a trait named `Draw` with one method named
305`draw`:
306
307Filename: src/lib.rs
308
309```
310pub trait Draw {
311 fn draw(&self);
312}
313```
314
315Listing 17-3: Definition of the `Draw` trait
316
317This syntax should look familiar from our discussions on how to define traits
318in Chapter 10. Next comes some new syntax: Listing 17-4 defines a struct named
319`Screen` that holds a vector named `components`. This vector is of type
320`Box<dyn Draw>`, which is a trait object; it’s a stand-in for any type inside
321a `Box` that implements the `Draw` trait.
322
323Filename: src/lib.rs
324
325```
326pub struct Screen {
327 pub components: Vec<Box<dyn Draw>>,
328}
329```
330
331Listing 17-4: Definition of the `Screen` struct with a `components` field
332holding a vector of trait objects that implement the `Draw` trait
333
334On the `Screen` struct, we’ll define a method named `run` that will call the
335`draw` method on each of its `components`, as shown in Listing 17-5:
336
337Filename: src/lib.rs
338
339```
340impl Screen {
341 pub fn run(&self) {
342 for component in self.components.iter() {
343 component.draw();
344 }
345 }
346}
347```
348
349Listing 17-5: A `run` method on `Screen` that calls the `draw` method on each
350component
351
352This works differently from defining a struct that uses a generic type
353parameter with trait bounds. A generic type parameter can only be substituted
354with one concrete type at a time, whereas trait objects allow for multiple
355concrete types to fill in for the trait object at runtime. For example, we
356could have defined the `Screen` struct using a generic type and a trait bound
357as in Listing 17-6:
358
359Filename: src/lib.rs
360
361```
362pub struct Screen<T: Draw> {
363 pub components: Vec<T>,
364}
365
366impl<T> Screen<T>
367where
368 T: Draw,
369{
370 pub fn run(&self) {
371 for component in self.components.iter() {
372 component.draw();
373 }
374 }
375}
376```
377
378Listing 17-6: An alternate implementation of the `Screen` struct and its `run`
379method using generics and trait bounds
380
381This restricts us to a `Screen` instance that has a list of components all of
382type `Button` or all of type `TextField`. If you’ll only ever have homogeneous
383collections, using generics and trait bounds is preferable because the
384definitions will be monomorphized at compile time to use the concrete types.
385
386On the other hand, with the method using trait objects, one `Screen` instance
387can hold a `Vec<T>` that contains a `Box<Button>` as well as a
388`Box<TextField>`. Let’s look at how this works, and then we’ll talk about the
389runtime performance implications.
390
391### Implementing the Trait
392
393Now we’ll add some types that implement the `Draw` trait. We’ll provide the
394`Button` type. Again, actually implementing a GUI library is beyond the scope
395of this book, so the `draw` method won’t have any useful implementation in its
396body. To imagine what the implementation might look like, a `Button` struct
397might have fields for `width`, `height`, and `label`, as shown in Listing 17-7:
398
399Filename: src/lib.rs
400
401```
402pub struct Button {
403 pub width: u32,
404 pub height: u32,
405 pub label: String,
406}
407
408impl Draw for Button {
409 fn draw(&self) {
410 // code to actually draw a button
411 }
412}
413```
414
415Listing 17-7: A `Button` struct that implements the `Draw` trait
416
417The `width`, `height`, and `label` fields on `Button` will differ from the
923072b8
FG
418fields on other components; for example, a `TextField` type might have those
419same fields plus a `placeholder` field. Each of the types we want to draw on
5099ac24
FG
420the screen will implement the `Draw` trait but will use different code in the
421`draw` method to define how to draw that particular type, as `Button` has here
923072b8
FG
422(without the actual GUI code, as mentioned). The `Button` type, for instance,
423might have an additional `impl` block containing methods related to what
424happens when a user clicks the button. These kinds of methods won’t apply to
425types like `TextField`.
5099ac24
FG
426
427If someone using our library decides to implement a `SelectBox` struct that has
428`width`, `height`, and `options` fields, they implement the `Draw` trait on the
429`SelectBox` type as well, as shown in Listing 17-8:
430
431Filename: src/main.rs
432
433```
434use gui::Draw;
435
436struct SelectBox {
437 width: u32,
438 height: u32,
439 options: Vec<String>,
440}
441
442impl Draw for SelectBox {
443 fn draw(&self) {
444 // code to actually draw a select box
445 }
446}
447```
448
449Listing 17-8: Another crate using `gui` and implementing the `Draw` trait on a
450`SelectBox` struct
451
452Our library’s user can now write their `main` function to create a `Screen`
453instance. To the `Screen` instance, they can add a `SelectBox` and a `Button`
454by putting each in a `Box<T>` to become a trait object. They can then call the
455`run` method on the `Screen` instance, which will call `draw` on each of the
456components. Listing 17-9 shows this implementation:
457
458Filename: src/main.rs
459
460```
461use gui::{Button, Screen};
462
463fn main() {
464 let screen = Screen {
465 components: vec![
466 Box::new(SelectBox {
467 width: 75,
468 height: 10,
469 options: vec![
470 String::from("Yes"),
471 String::from("Maybe"),
472 String::from("No"),
473 ],
474 }),
475 Box::new(Button {
476 width: 50,
477 height: 10,
478 label: String::from("OK"),
479 }),
480 ],
481 };
482
483 screen.run();
484}
485```
486
923072b8
FG
487<!-- I'd forgotten the UI components were in this chapter. To close on the
488thought from earlier: we don't use any inheritance in our example, only
489polymorphism. This probably is a vote for my earlier suggestion.
490/JT -->
491<!-- I indeed took the earlier suggestion. /Carol -->
492
5099ac24
FG
493Listing 17-9: Using trait objects to store values of different types that
494implement the same trait
495
496When we wrote the library, we didn’t know that someone might add the
497`SelectBox` type, but our `Screen` implementation was able to operate on the
498new type and draw it because `SelectBox` implements the `Draw` trait, which
499means it implements the `draw` method.
500
501This concept—of being concerned only with the messages a value responds to
502rather than the value’s concrete type—is similar to the concept of *duck
503typing* in dynamically typed languages: if it walks like a duck and quacks
504like a duck, then it must be a duck! In the implementation of `run` on `Screen`
505in Listing 17-5, `run` doesn’t need to know what the concrete type of each
506component is. It doesn’t check whether a component is an instance of a `Button`
507or a `SelectBox`, it just calls the `draw` method on the component. By
508specifying `Box<dyn Draw>` as the type of the values in the `components`
509vector, we’ve defined `Screen` to need values that we can call the `draw`
510method on.
511
512The advantage of using trait objects and Rust’s type system to write code
513similar to code using duck typing is that we never have to check whether a
514value implements a particular method at runtime or worry about getting errors
515if a value doesn’t implement a method but we call it anyway. Rust won’t compile
516our code if the values don’t implement the traits that the trait objects need.
517
518For example, Listing 17-10 shows what happens if we try to create a `Screen`
519with a `String` as a component:
520
521Filename: src/main.rs
522
523```
524use gui::Screen;
525
526fn main() {
527 let screen = Screen {
528 components: vec![Box::new(String::from("Hi"))],
529 };
530
531 screen.run();
532}
533```
534
535Listing 17-10: Attempting to use a type that doesn’t
536implement the trait object’s trait
537
538We’ll get this error because `String` doesn’t implement the `Draw` trait:
539
540```
541error[E0277]: the trait bound `String: Draw` is not satisfied
542 --> src/main.rs:5:26
543 |
5445 | components: vec![Box::new(String::from("Hi"))],
545 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Draw` is not implemented for `String`
546 |
547 = note: required for the cast to the object type `dyn Draw`
548```
549
550This error lets us know that either we’re passing something to `Screen` we
923072b8 551didn’t mean to pass and so should pass a different type or we should implement
5099ac24
FG
552`Draw` on `String` so that `Screen` is able to call `draw` on it.
553
554### Trait Objects Perform Dynamic Dispatch
555
556Recall in the “Performance of Code Using Generics” section in Chapter 10 our
557discussion on the monomorphization process performed by the compiler when we
558use trait bounds on generics: the compiler generates nongeneric implementations
559of functions and methods for each concrete type that we use in place of a
560generic type parameter. The code that results from monomorphization is doing
561*static dispatch*, which is when the compiler knows what method you’re calling
562at compile time. This is opposed to *dynamic dispatch*, which is when the
563compiler can’t tell at compile time which method you’re calling. In dynamic
564dispatch cases, the compiler emits code that at runtime will figure out which
565method to call.
566
567When we use trait objects, Rust must use dynamic dispatch. The compiler doesn’t
923072b8
FG
568know all the types that might be used with the code that’s using trait objects,
569so it doesn’t know which method implemented on which type to call. Instead, at
570runtime, Rust uses the pointers inside the trait object to know which method to
571call. This lookup incurs a runtime cost that doesn’t occur with static
572dispatch. Dynamic dispatch also prevents the compiler from choosing to inline a
573method’s code, which in turn prevents some optimizations. However, we did get
574extra flexibility in the code that we wrote in Listing 17-5 and were able to
575support in Listing 17-9, so it’s a trade-off to consider.
5099ac24
FG
576
577## Implementing an Object-Oriented Design Pattern
578
579The *state pattern* is an object-oriented design pattern. The crux of the
923072b8
FG
580pattern is that we define a set of states a value can have internally. The
581states are represented by a set of *state objects*, and the value’s behavior
582changes based on its state. We’re going to work through an example of a blog
583post struct that has a field to hold its state, which will be a state object
584from the set "draft", "review", or "published".
585<!-- can you give a quick example here, something we could visualize? are we
586saying "we define a set of states a value can have as state objects...."? /LC
587-->
588<!-- What do you think about this, hinting at the coming example quickly? It
589felt weird to introduce something different only to switch gears in a few
590paragraphs, so is moving the example's introduction here ok? /Carol -->
591<!-- JT, what do you think? /LC -->
592<!-- Seems okay. My one thought coming to the end of the paragraph was "is
593this better than using an enum?" Not sure if we want to sidebar a bit on
594why we chose traits over enums, but some readers might be curious.
595/JT -->
596<!-- I've added a box later titled "Why Not An Enum?" to address this -- I
597think that makes a nice exercise for the reader :) /Carol -->
598
599The state objects share functionality: in Rust, of course, we use
5099ac24
FG
600structs and traits rather than objects and inheritance. Each state object is
601responsible for its own behavior and for governing when it should change into
602another state. The value that holds a state object knows nothing about the
603different behavior of the states or when to transition between states.
604
923072b8
FG
605The advantage of using the state pattern is that, when the business
606requirements of the program change, we won’t need to change the code of the
607value holding the state or the code that uses the value. We’ll only need to
608update the code inside one of the state objects to change its rules or perhaps
609add more state objects.
610
611First, we’re going to implement the state pattern in a more traditional
612object-oriented way, then we’ll use an approach that’s a bit more natural in
613Rust. Let’s dig in to incrementally implementing a blog post workflow using the
614state pattern.
5099ac24 615
923072b8 616The final functionality will look like this:
5099ac24
FG
617
6181. A blog post starts as an empty draft.
6192. When the draft is done, a review of the post is requested.
6203. When the post is approved, it gets published.
6214. Only published blog posts return content to print, so unapproved posts can’t
622 accidentally be published.
623
624Any other changes attempted on a post should have no effect. For example, if we
625try to approve a draft blog post before we’ve requested a review, the post
626should remain an unpublished draft.
627
628Listing 17-11 shows this workflow in code form: this is an example usage of the
629API we’ll implement in a library crate named `blog`. This won’t compile yet
923072b8 630because we haven’t implemented the `blog` crate.
5099ac24
FG
631
632Filename: src/main.rs
633
634```
635use blog::Post;
636
637fn main() {
638 [1] let mut post = Post::new();
639
640 [2] post.add_text("I ate a salad for lunch today");
641 [3] assert_eq!("", post.content());
642
643 [4] post.request_review();
644 [5] assert_eq!("", post.content());
645
646 [6] post.approve();
647 [7] assert_eq!("I ate a salad for lunch today", post.content());
648}
649```
650
651Listing 17-11: Code that demonstrates the desired behavior we want our `blog`
652crate to have
653
654We want to allow the user to create a new draft blog post with `Post::new` [1].
655We want to allow text to be added to the blog post [2]. If we try to get the
923072b8 656post’s content immediately, before approval, we shouldn’t get any text because
5099ac24
FG
657the post is still a draft. We’ve added `assert_eq!` in the code for
658demonstration purposes [3]. An excellent unit test for this would be to assert
659that a draft blog post returns an empty string from the `content` method, but
660we’re not going to write tests for this example.
661
662Next, we want to enable a request for a review of the post [4], and we want
663`content` to return an empty string while waiting for the review [5]. When the
664post receives approval [6], it should get published, meaning the text of the
665post will be returned when `content` is called [7].
666
667Notice that the only type we’re interacting with from the crate is the `Post`
668type. This type will use the state pattern and will hold a value that will be
669one of three state objects representing the various states a post can be
670in—draft, waiting for review, or published. Changing from one state to another
671will be managed internally within the `Post` type. The states change in
672response to the methods called by our library’s users on the `Post` instance,
673but they don’t have to manage the state changes directly. Also, users can’t
674make a mistake with the states, like publishing a post before it’s reviewed.
675
676### Defining `Post` and Creating a New Instance in the Draft State
677
678Let’s get started on the implementation of the library! We know we need a
679public `Post` struct that holds some content, so we’ll start with the
680definition of the struct and an associated public `new` function to create an
681instance of `Post`, as shown in Listing 17-12. We’ll also make a private
923072b8
FG
682`State` trait that will define the behavior that all state objects for a `Post`
683must have.
684<!-- JT, I had a few questions here about what the state objects and state
685traits are doing. I'd appreciate your view on whether this all reads well with
686nothing missing! /LC -->
687<!-- Seems okay. If you're going to try to use a traditional OO approach in
688Rust, it'll have a bit of this style. I'm glad we include something that's a
689bit more Rust-y at the end of the chapter.
690
691What I might suggest is that we give the reader a bit of a roadmap here to say
692that we're going to explore two solutions to this problem. The first, a more
693traditional approach encoded into Rust, and the second, an approach that's more
694natural to Rust.
695/JT -->
696<!-- Great idea! I've added a bit in the introduction of this section above --
697"First, we’re going to implement the state pattern in a more traditional
698object-oriented way, then we’ll use an approach that’s a bit more natural in
699Rust." /Carol -->
700
701Then `Post` will hold a trait object of `Box<dyn State>` inside an `Option<T>`
702in a private field named `state` to hold the state object. You’ll see why the
5099ac24
FG
703`Option<T>` is necessary in a bit.
704
705Filename: src/lib.rs
706
707```
708pub struct Post {
709 state: Option<Box<dyn State>>,
710 content: String,
711}
712
713impl Post {
714 pub fn new() -> Post {
715 Post {
716 [1] state: Some(Box::new(Draft {})),
717 [2] content: String::new(),
718 }
719 }
720}
721
722trait State {}
723
724struct Draft {}
725
726impl State for Draft {}
727```
728
729Listing 17-12: Definition of a `Post` struct and a `new` function that creates
730a new `Post` instance, a `State` trait, and a `Draft` struct
731
923072b8
FG
732The `State` trait defines the behavior shared by different post states. The
733state objects are `Draft`, `PendingReview`, and `Published`, and they will all
734implement the `State` trait. For now, the trait doesn’t have any methods, and
735we’ll start by defining just the `Draft` state because that is the state we
736want a post to start in.
5099ac24
FG
737
738When we create a new `Post`, we set its `state` field to a `Some` value that
739holds a `Box` [1]. This `Box` points to a new instance of the `Draft` struct.
740This ensures whenever we create a new instance of `Post`, it will start out as
741a draft. Because the `state` field of `Post` is private, there is no way to
742create a `Post` in any other state! In the `Post::new` function, we set the
743`content` field to a new, empty `String` [2].
744
745### Storing the Text of the Post Content
746
923072b8
FG
747We saw in Listing 17-11 that we want to be able to call a method named
748`add_text` and pass it a `&str` that is then added as the text content of the
749blog post. We implement this as a method, rather than exposing the `content`
750field as `pub`, so that later we can implement a method that will control how
751the `content` field’s data is read. The `add_text` method is pretty
5099ac24
FG
752straightforward, so let’s add the implementation in Listing 17-13 to the `impl
753Post` block:
754
755Filename: src/lib.rs
756
757```
758impl Post {
759 // --snip--
760 pub fn add_text(&mut self, text: &str) {
761 self.content.push_str(text);
762 }
763}
764```
765
766Listing 17-13: Implementing the `add_text` method to add text to a post’s
767`content`
768
769The `add_text` method takes a mutable reference to `self`, because we’re
770changing the `Post` instance that we’re calling `add_text` on. We then call
771`push_str` on the `String` in `content` and pass the `text` argument to add to
772the saved `content`. This behavior doesn’t depend on the state the post is in,
773so it’s not part of the state pattern. The `add_text` method doesn’t interact
774with the `state` field at all, but it is part of the behavior we want to
775support.
776
777### Ensuring the Content of a Draft Post Is Empty
778
779Even after we’ve called `add_text` and added some content to our post, we still
780want the `content` method to return an empty string slice because the post is
781still in the draft state, as shown at [3] in Listing 17-11. For now, let’s
782implement the `content` method with the simplest thing that will fulfill this
783requirement: always returning an empty string slice. We’ll change this later
784once we implement the ability to change a post’s state so it can be published.
785So far, posts can only be in the draft state, so the post content should always
786be empty. Listing 17-14 shows this placeholder implementation:
787
788Filename: src/lib.rs
789
790```
791impl Post {
792 // --snip--
793 pub fn content(&self) -> &str {
794 ""
795 }
796}
797```
798
799Listing 17-14: Adding a placeholder implementation for the `content` method on
800`Post` that always returns an empty string slice
801
802With this added `content` method, everything in Listing 17-11 up to the line at
803[3] works as intended.
804
805### Requesting a Review of the Post Changes Its State
806
807Next, we need to add functionality to request a review of a post, which should
808change its state from `Draft` to `PendingReview`. Listing 17-15 shows this code:
809
810Filename: src/lib.rs
811
812```
813impl Post {
814 // --snip--
815 [1] pub fn request_review(&mut self) {
816 [2] if let Some(s) = self.state.take() {
817 [3] self.state = Some(s.request_review())
818 }
819 }
820}
821
822trait State {
823 [4] fn request_review(self: Box<Self>) -> Box<dyn State>;
824}
825
826struct Draft {}
827
828impl State for Draft {
829 fn request_review(self: Box<Self>) -> Box<dyn State> {
830 [5] Box::new(PendingReview {})
831 }
832}
833
834struct PendingReview {}
835
836impl State for PendingReview {
837 fn request_review(self: Box<Self>) -> Box<dyn State> {
838 [6] self
839 }
840}
841```
842
843Listing 17-15: Implementing `request_review` methods on `Post` and the `State`
844trait
845
846We give `Post` a public method named `request_review` that will take a mutable
847reference to `self` [1]. Then we call an internal `request_review` method on
848the current state of `Post` [3], and this second `request_review` method
849consumes the current state and returns a new state.
850
923072b8
FG
851We add the `request_review` method to the `State` trait [4]; all types that
852implement the trait will now need to implement the `request_review` method.
853Note that rather than having `self`, `&self`, or `&mut self` as the first
854parameter of the method, we have `self: Box<Self>`. This syntax means the
5099ac24
FG
855method is only valid when called on a `Box` holding the type. This syntax takes
856ownership of `Box<Self>`, invalidating the old state so the state value of the
857`Post` can transform into a new state.
858
859To consume the old state, the `request_review` method needs to take ownership
860of the state value. This is where the `Option` in the `state` field of `Post`
861comes in: we call the `take` method to take the `Some` value out of the `state`
862field and leave a `None` in its place, because Rust doesn’t let us have
863unpopulated fields in structs [2]. This lets us move the `state` value out of
864`Post` rather than borrowing it. Then we’ll set the post’s `state` value to the
865result of this operation.
866
867We need to set `state` to `None` temporarily rather than setting it directly
868with code like `self.state = self.state.request_review();` to get ownership of
869the `state` value. This ensures `Post` can’t use the old `state` value after
870we’ve transformed it into a new state.
871
923072b8
FG
872The `request_review` method on `Draft` returns a new, boxed instance of a new
873`PendingReview` struct [5], which represents the state when a post is waiting
874for a review. The `PendingReview` struct also implements the `request_review`
875method but doesn’t do any transformations. Rather, it returns itself [6],
876because when we request a review on a post already in the `PendingReview`
877state, it should stay in the `PendingReview` state.
5099ac24
FG
878
879Now we can start seeing the advantages of the state pattern: the
880`request_review` method on `Post` is the same no matter its `state` value. Each
881state is responsible for its own rules.
882
883We’ll leave the `content` method on `Post` as is, returning an empty string
884slice. We can now have a `Post` in the `PendingReview` state as well as in the
885`Draft` state, but we want the same behavior in the `PendingReview` state.
886Listing 17-11 now works up to the line at [5]!
887
923072b8 888### Adding `approve` to Change the Behavior of `content`
5099ac24
FG
889
890The `approve` method will be similar to the `request_review` method: it will
891set `state` to the value that the current state says it should have when that
892state is approved, as shown in Listing 17-16:
893
894Filename: src/lib.rs
895
896```
897impl Post {
898 // --snip--
899 pub fn approve(&mut self) {
900 if let Some(s) = self.state.take() {
901 self.state = Some(s.approve())
902 }
903 }
904}
905
906trait State {
907 fn request_review(self: Box<Self>) -> Box<dyn State>;
908 fn approve(self: Box<Self>) -> Box<dyn State>;
909}
910
911struct Draft {}
912
913impl State for Draft {
914 // --snip--
915 fn approve(self: Box<Self>) -> Box<dyn State> {
916 [1] self
917 }
918}
919
920struct PendingReview {}
921
922impl State for PendingReview {
923 // --snip--
924 fn approve(self: Box<Self>) -> Box<dyn State> {
925 [2] Box::new(Published {})
926 }
927}
928
929struct Published {}
930
931impl State for Published {
932 fn request_review(self: Box<Self>) -> Box<dyn State> {
933 self
934 }
935
936 fn approve(self: Box<Self>) -> Box<dyn State> {
937 self
938 }
939}
940```
941
942Listing 17-16: Implementing the `approve` method on `Post` and the `State` trait
943
944We add the `approve` method to the `State` trait and add a new struct that
945implements `State`, the `Published` state.
946
947Similar to the way `request_review` on `PendingReview` works, if we call the
948`approve` method on a `Draft`, it will have no effect because `approve` will
949return `self` [1]. When we call `approve` on `PendingReview`, it returns a new,
950boxed instance of the `Published` struct [2]. The `Published` struct implements
951the `State` trait, and for both the `request_review` method and the `approve`
952method, it returns itself, because the post should stay in the `Published`
953state in those cases.
954
955Now we need to update the `content` method on `Post`. We want the value
923072b8 956returned from `content` to depend on the current state of the `Post`, so we’re
5099ac24
FG
957going to have the `Post` delegate to a `content` method defined on its `state`,
958as shown in Listing 17-17:
959
960Filename: src/lib.rs
961
962```
963impl Post {
964 // --snip--
965 pub fn content(&self) -> &str {
966 self.state.as_ref().unwrap().content(self)
967 }
968 // --snip--
969}
970```
971
972Listing 17-17: Updating the `content` method on `Post` to delegate to a
973`content` method on `State`
974
975Because the goal is to keep all these rules inside the structs that implement
976`State`, we call a `content` method on the value in `state` and pass the post
923072b8 977instance (that is, `self`) as an argument. Then we return the value that’s
5099ac24
FG
978returned from using the `content` method on the `state` value.
979
980We call the `as_ref` method on the `Option` because we want a reference to the
981value inside the `Option` rather than ownership of the value. Because `state`
982is an `Option<Box<dyn State>>`, when we call `as_ref`, an `Option<&Box<dyn
983State>>` is returned. If we didn’t call `as_ref`, we would get an error because
984we can’t move `state` out of the borrowed `&self` of the function parameter.
985
986We then call the `unwrap` method, which we know will never panic, because we
987know the methods on `Post` ensure that `state` will always contain a `Some`
988value when those methods are done. This is one of the cases we talked about in
923072b8
FG
989the “Cases In Which You Have More Information Than the Compiler” section of
990Chapter 9 when we know that a `None` value is never possible, even though the
991compiler isn’t able to understand that.
5099ac24
FG
992
993At this point, when we call `content` on the `&Box<dyn State>`, deref coercion
994will take effect on the `&` and the `Box` so the `content` method will
995ultimately be called on the type that implements the `State` trait. That means
996we need to add `content` to the `State` trait definition, and that is where
997we’ll put the logic for what content to return depending on which state we
998have, as shown in Listing 17-18:
999
1000Filename: src/lib.rs
1001
1002```
1003trait State {
1004 // --snip--
1005 fn content<'a>(&self, post: &'a Post) -> &'a str {
1006 [1] ""
1007 }
1008}
1009
1010// --snip--
1011struct Published {}
1012
1013impl State for Published {
1014 // --snip--
1015 fn content<'a>(&self, post: &'a Post) -> &'a str {
1016 [2] &post.content
1017 }
1018}
1019```
1020
1021Listing 17-18: Adding the `content` method to the `State` trait
1022
1023We add a default implementation for the `content` method that returns an empty
1024string slice [1]. That means we don’t need to implement `content` on the `Draft`
1025and `PendingReview` structs. The `Published` struct will override the `content`
1026method and return the value in `post.content` [2].
1027
1028Note that we need lifetime annotations on this method, as we discussed in
1029Chapter 10. We’re taking a reference to a `post` as an argument and returning a
1030reference to part of that `post`, so the lifetime of the returned reference is
1031related to the lifetime of the `post` argument.
1032
1033And we’re done—all of Listing 17-11 now works! We’ve implemented the state
1034pattern with the rules of the blog post workflow. The logic related to the
1035rules lives in the state objects rather than being scattered throughout `Post`.
1036
923072b8
FG
1037> #### Why Not An Enum?
1038>
1039> You may have been wondering why we didn’t use an `enum` with the different
1040> possible post states as variants. That’s certainly a possible solution, try
1041> it and compare the end results to see which you prefer! One disadvantage of
1042> using an enum is every place that checks the value of the enum will need a
1043> `match` expression or similar to handle every possible variant. This could
1044> get more repetitive than this trait object solution.
1045
5099ac24
FG
1046### Trade-offs of the State Pattern
1047
1048We’ve shown that Rust is capable of implementing the object-oriented state
1049pattern to encapsulate the different kinds of behavior a post should have in
1050each state. The methods on `Post` know nothing about the various behaviors. The
1051way we organized the code, we have to look in only one place to know the
1052different ways a published post can behave: the implementation of the `State`
1053trait on the `Published` struct.
1054
1055If we were to create an alternative implementation that didn’t use the state
1056pattern, we might instead use `match` expressions in the methods on `Post` or
1057even in the `main` code that checks the state of the post and changes behavior
1058in those places. That would mean we would have to look in several places to
1059understand all the implications of a post being in the published state! This
1060would only increase the more states we added: each of those `match` expressions
1061would need another arm.
1062
1063With the state pattern, the `Post` methods and the places we use `Post` don’t
1064need `match` expressions, and to add a new state, we would only need to add a
1065new struct and implement the trait methods on that one struct.
1066
1067The implementation using the state pattern is easy to extend to add more
1068functionality. To see the simplicity of maintaining code that uses the state
1069pattern, try a few of these suggestions:
1070
1071* Add a `reject` method that changes the post’s state from `PendingReview` back
1072 to `Draft`.
1073* Require two calls to `approve` before the state can be changed to `Published`.
1074* Allow users to add text content only when a post is in the `Draft` state.
1075 Hint: have the state object responsible for what might change about the
1076 content but not responsible for modifying the `Post`.
1077
1078One downside of the state pattern is that, because the states implement the
1079transitions between states, some of the states are coupled to each other. If we
1080add another state between `PendingReview` and `Published`, such as `Scheduled`,
1081we would have to change the code in `PendingReview` to transition to
1082`Scheduled` instead. It would be less work if `PendingReview` didn’t need to
1083change with the addition of a new state, but that would mean switching to
1084another design pattern.
1085
1086Another downside is that we’ve duplicated some logic. To eliminate some of the
1087duplication, we might try to make default implementations for the
1088`request_review` and `approve` methods on the `State` trait that return `self`;
1089however, this would violate object safety, because the trait doesn’t know what
1090the concrete `self` will be exactly. We want to be able to use `State` as a
1091trait object, so we need its methods to be object safe.
1092
1093Other duplication includes the similar implementations of the `request_review`
1094and `approve` methods on `Post`. Both methods delegate to the implementation of
1095the same method on the value in the `state` field of `Option` and set the new
1096value of the `state` field to the result. If we had a lot of methods on `Post`
1097that followed this pattern, we might consider defining a macro to eliminate the
1098repetition (see the “Macros” section in Chapter 19).
1099
1100By implementing the state pattern exactly as it’s defined for object-oriented
1101languages, we’re not taking as full advantage of Rust’s strengths as we could.
1102Let’s look at some changes we can make to the `blog` crate that can make
1103invalid states and transitions into compile time errors.
1104
1105#### Encoding States and Behavior as Types
1106
1107We’ll show you how to rethink the state pattern to get a different set of
1108trade-offs. Rather than encapsulating the states and transitions completely so
1109outside code has no knowledge of them, we’ll encode the states into different
1110types. Consequently, Rust’s type checking system will prevent attempts to use
1111draft posts where only published posts are allowed by issuing a compiler error.
1112
1113Let’s consider the first part of `main` in Listing 17-11:
1114
1115Filename: src/main.rs
1116
1117```
1118fn main() {
1119 let mut post = Post::new();
1120
1121 post.add_text("I ate a salad for lunch today");
1122 assert_eq!("", post.content());
1123}
1124```
1125
1126We still enable the creation of new posts in the draft state using `Post::new`
1127and the ability to add text to the post’s content. But instead of having a
1128`content` method on a draft post that returns an empty string, we’ll make it so
1129draft posts don’t have the `content` method at all. That way, if we try to get
1130a draft post’s content, we’ll get a compiler error telling us the method
1131doesn’t exist. As a result, it will be impossible for us to accidentally
1132display draft post content in production, because that code won’t even compile.
1133Listing 17-19 shows the definition of a `Post` struct and a `DraftPost` struct,
1134as well as methods on each:
1135
1136Filename: src/lib.rs
1137
1138```
1139pub struct Post {
1140 content: String,
1141}
1142
1143pub struct DraftPost {
1144 content: String,
1145}
1146
1147impl Post {
1148 [1] pub fn new() -> DraftPost {
1149 DraftPost {
1150 content: String::new(),
1151 }
1152 }
1153
1154 [2] pub fn content(&self) -> &str {
1155 &self.content
1156 }
1157}
1158
1159impl DraftPost {
1160 [3] pub fn add_text(&mut self, text: &str) {
1161 self.content.push_str(text);
1162 }
1163}
1164```
1165
1166Listing 17-19: A `Post` with a `content` method and a `DraftPost` without a
1167`content` method
1168
1169Both the `Post` and `DraftPost` structs have a private `content` field that
1170stores the blog post text. The structs no longer have the `state` field because
1171we’re moving the encoding of the state to the types of the structs. The `Post`
1172struct will represent a published post, and it has a `content` method that
1173returns the `content` [2].
1174
1175We still have a `Post::new` function, but instead of returning an instance of
1176`Post`, it returns an instance of `DraftPost` [1]. Because `content` is private
1177and there aren’t any functions that return `Post`, it’s not possible to create
1178an instance of `Post` right now.
1179
1180The `DraftPost` struct has an `add_text` method, so we can add text to
1181`content` as before [3], but note that `DraftPost` does not have a `content`
1182method defined! So now the program ensures all posts start as draft posts, and
1183draft posts don’t have their content available for display. Any attempt to get
1184around these constraints will result in a compiler error.
1185
1186#### Implementing Transitions as Transformations into Different Types
1187
1188So how do we get a published post? We want to enforce the rule that a draft
1189post has to be reviewed and approved before it can be published. A post in the
1190pending review state should still not display any content. Let’s implement
1191these constraints by adding another struct, `PendingReviewPost`, defining the
1192`request_review` method on `DraftPost` to return a `PendingReviewPost`, and
1193defining an `approve` method on `PendingReviewPost` to return a `Post`, as
1194shown in Listing 17-20:
1195
1196Filename: src/lib.rs
1197
1198```
1199impl DraftPost {
1200 // --snip--
1201 pub fn request_review(self) -> PendingReviewPost {
1202 PendingReviewPost {
1203 content: self.content,
1204 }
1205 }
1206}
1207
1208pub struct PendingReviewPost {
1209 content: String,
1210}
1211
1212impl PendingReviewPost {
1213 pub fn approve(self) -> Post {
1214 Post {
1215 content: self.content,
1216 }
1217 }
1218}
1219```
1220
1221Listing 17-20: A `PendingReviewPost` that gets created by calling
1222`request_review` on `DraftPost` and an `approve` method that turns a
1223`PendingReviewPost` into a published `Post`
1224
1225The `request_review` and `approve` methods take ownership of `self`, thus
1226consuming the `DraftPost` and `PendingReviewPost` instances and transforming
1227them into a `PendingReviewPost` and a published `Post`, respectively. This way,
1228we won’t have any lingering `DraftPost` instances after we’ve called
1229`request_review` on them, and so forth. The `PendingReviewPost` struct doesn’t
1230have a `content` method defined on it, so attempting to read its content
1231results in a compiler error, as with `DraftPost`. Because the only way to get a
1232published `Post` instance that does have a `content` method defined is to call
1233the `approve` method on a `PendingReviewPost`, and the only way to get a
1234`PendingReviewPost` is to call the `request_review` method on a `DraftPost`,
1235we’ve now encoded the blog post workflow into the type system.
1236
1237But we also have to make some small changes to `main`. The `request_review` and
1238`approve` methods return new instances rather than modifying the struct they’re
1239called on, so we need to add more `let post =` shadowing assignments to save
1240the returned instances. We also can’t have the assertions about the draft and
1241pending review posts’ contents be empty strings, nor do we need them: we can’t
1242compile code that tries to use the content of posts in those states any longer.
1243The updated code in `main` is shown in Listing 17-21:
1244
1245Filename: src/main.rs
1246
1247```
1248use blog::Post;
1249
1250fn main() {
1251 let mut post = Post::new();
1252
1253 post.add_text("I ate a salad for lunch today");
1254
1255 let post = post.request_review();
1256
1257 let post = post.approve();
1258
1259 assert_eq!("I ate a salad for lunch today", post.content());
1260}
1261```
1262
1263Listing 17-21: Modifications to `main` to use the new implementation of the
1264blog post workflow
1265
1266The changes we needed to make to `main` to reassign `post` mean that this
1267implementation doesn’t quite follow the object-oriented state pattern anymore:
1268the transformations between the states are no longer encapsulated entirely
1269within the `Post` implementation. However, our gain is that invalid states are
1270now impossible because of the type system and the type checking that happens at
1271compile time! This ensures that certain bugs, such as display of the content of
1272an unpublished post, will be discovered before they make it to production.
1273
923072b8
FG
1274Try the tasks suggested at the start of this section on the `blog` crate as it
1275is after Listing 17-21 to see what you think about the design of this version
1276of the code. Note that some of the tasks might be completed already in this
1277design.
5099ac24
FG
1278
1279We’ve seen that even though Rust is capable of implementing object-oriented
1280design patterns, other patterns, such as encoding state into the type system,
1281are also available in Rust. These patterns have different trade-offs. Although
1282you might be very familiar with object-oriented patterns, rethinking the
1283problem to take advantage of Rust’s features can provide benefits, such as
1284preventing some bugs at compile time. Object-oriented patterns won’t always be
1285the best solution in Rust due to certain features, like ownership, that
1286object-oriented languages don’t have.
1287
1288## Summary
1289
1290No matter whether or not you think Rust is an object-oriented language after
1291reading this chapter, you now know that you can use trait objects to get some
1292object-oriented features in Rust. Dynamic dispatch can give your code some
1293flexibility in exchange for a bit of runtime performance. You can use this
1294flexibility to implement object-oriented patterns that can help your code’s
1295maintainability. Rust also has other features, like ownership, that
1296object-oriented languages don’t have. An object-oriented pattern won’t always
1297be the best way to take advantage of Rust’s strengths, but is an available
1298option.
1299
1300Next, we’ll look at patterns, which are another of Rust’s features that enable
1301lots of flexibility. We’ve looked at them briefly throughout the book but
1302haven’t seen their full capability yet. Let’s go!