]> git.proxmox.com Git - rustc.git/blame - src/doc/book/second-edition/src/ch17-01-what-is-oo.md
New upstream version 1.22.1+dfsg1
[rustc.git] / src / doc / book / second-edition / src / ch17-01-what-is-oo.md
CommitLineData
cc61c64b
XL
1## What Does Object-Oriented Mean?
2
3b2f2976 3There isn’t consensus in the programming community about the features a
cc61c64b
XL
4language needs to have in order to be called object-oriented. Rust is
5influenced by many different programming paradigms; we explored the features it
6has that come from functional programming in Chapter 13. Some of the
7characteristics that object-oriented programming languages tend to share are
3b2f2976 8objects, encapsulation, and inheritance. Let’s take a look at what each of
cc61c64b
XL
9those mean and whether Rust supports them.
10
11### Objects Contain Data and Behavior
12
3b2f2976
XL
13The book “Design Patterns: Elements of Reusable Object-Oriented Software,”
14colloquially referred to as “The Gang of Four book,” is a catalog of
cc61c64b
XL
15object-oriented design patterns. It defines object-oriented programming in this
16way:
17
18> Object-oriented programs are made up of objects. An *object* packages both
19> data and the procedures that operate on that data. The procedures are
20> typically called *methods* or *operations*.
21
22Under this definition, then, Rust is object-oriented: structs and enums have
23data and `impl` blocks provide methods on structs and enums. Even though
3b2f2976
XL
24structs and enums with methods aren’t *called* objects, they provide the same
25functionality that objects do, using the Gang of Four’s definition of objects.
cc61c64b
XL
26
27### Encapsulation that Hides Implementation Details
28
29Another aspect commonly associated with object-oriented programming is the idea
3b2f2976 30of *encapsulation*: the implementation details of an object aren’t accessible
cc61c64b
XL
31to code using that object. The only way to interact with an object is through
32the public API the object offers; code using the object should not be able to
3b2f2976
XL
33reach into the object’s internals and change data or behavior directly.
34Encapsulation enables changing and refactoring an object’s internals without
cc61c64b
XL
35needing to change the code that uses the object.
36
37As we discussed in Chapter 7, we can use the `pub` keyword to decide what
38modules, types, functions, and methods in our code should be public, and by
39default, everything is private. For example, we can define a struct
40`AveragedCollection` that has a field containing a vector of `i32` values. The
41struct can also have a field that knows the average of the values in the vector
42so that whenever anyone wants to know the average of the values that the struct
3b2f2976 43has in its vector, we don’t have to compute it on-demand. `AveragedCollection`
cc61c64b
XL
44will cache the calculated average for us. Listing 17-1 has the definition of
45the `AveragedCollection` struct:
46
47<span class="filename">Filename: src/lib.rs</span>
48
49```rust
50pub struct AveragedCollection {
51 list: Vec<i32>,
52 average: f64,
53}
54```
55
56<span class="caption">Listing 17-1: An `AveragedCollection` struct that
57maintains a list of integers and the average of the items in the
58collection.</span>
59
60Note that the struct itself is marked `pub` so that other code may use this
61struct, but the fields within the struct remain private. This is important in
62this case because we want to ensure that whenever a value is added or removed
63from the list, we also update the average. We do this by implementing `add`,
64`remove`, and `average` methods on the struct as shown in Listing 17-2:
65
66<span class="filename">Filename: src/lib.rs</span>
67
68```rust
69# pub struct AveragedCollection {
70# list: Vec<i32>,
71# average: f64,
72# }
73impl AveragedCollection {
74 pub fn add(&mut self, value: i32) {
75 self.list.push(value);
76 self.update_average();
77 }
78
79 pub fn remove(&mut self) -> Option<i32> {
80 let result = self.list.pop();
81 match result {
82 Some(value) => {
83 self.update_average();
84 Some(value)
85 },
86 None => None,
87 }
88 }
89
90 pub fn average(&self) -> f64 {
91 self.average
92 }
93
94 fn update_average(&mut self) {
95 let total: i32 = self.list.iter().sum();
96 self.average = total as f64 / self.list.len() as f64;
97 }
98}
99```
100
101<span class="caption">Listing 17-2: Implementations of the public methods
102`add`, `remove`, and `average` on `AveragedCollection`</span>
103
104The public methods `add`, `remove`, and `average` are the only way to modify an
105instance of a `AveragedCollection`. When an item is added to `list` using the
106`add` method or removed using the `remove` method, the implementations of those
107methods call the private `update_average` method that takes care of updating
108the `average` field as well. Because the `list` and `average` fields are
3b2f2976 109private, there’s no way for external code to add or remove items to the `list`
cc61c64b
XL
110field directly, which could cause the `average` field to get out of sync. The
111`average` method returns the value in the `average` field, which allows
112external code to read the `average` but not modify it.
113
3b2f2976 114Because we’ve encapsulated the implementation details of `AveragedCollection`,
cc61c64b
XL
115we can easily change aspects like the data structure in the future. For
116instance, we could use a `HashSet` instead of a `Vec` for the `list` field. As
117long as the signatures of the `add`, `remove`, and `average` public methods
3b2f2976
XL
118stay the same, code using `AveragedCollection` wouldn’t need to change. This
119wouldn’t necessarily be the case if we exposed `list` to external code:
cc61c64b
XL
120`HashSet` and `Vec` have different methods for adding and removing items, so
121the external code would likely have to change if it was modifying `list`
122directly.
123
124If encapsulation is a required aspect for a language to be considered
125object-oriented, then Rust meets that requirement. Using `pub` or not for
126different parts of code enables encapsulation of implementation details.
127
128### Inheritance as a Type System and as Code Sharing
129
130*Inheritance* is a mechanism that some programming languages provide whereby an
3b2f2976
XL
131object can be defined to inherit from another object’s definition, thus gaining
132the parent object’s data and behavior without having to define those again.
133Inheritance is a characteristic that is part of some people’s definitions of
cc61c64b
XL
134what an OOP language is.
135
136If a language must have inheritance to be an object-oriented language, then
137Rust is not object-oriented. There is not a way to define a struct that
3b2f2976
XL
138inherits from another struct in order to gain the parent struct’s fields and
139method implementations. However, if you’re used to having inheritance in your
cc61c64b
XL
140programming toolbox, there are other solutions in Rust depending on the reason
141you want to use inheritance.
142
143There are two main reasons to reach for inheritance. The first is to be able to
144re-use code: once a particular behavior is implemented for one type,
145inheritance can enable re-using that implementation for a different type. Rust
146code can be shared using default trait method implementations instead, which we
3b2f2976 147saw in Listing 10-15 when we added a default implementation of the `summary`
cc61c64b
XL
148method on the `Summarizable` trait. Any type implementing the `Summarizable`
149trait would have the `summary` method available on it without any further code.
150This is similar to a parent class having an implementation of a method, and a
151child class inheriting from the parent class also having the implementation of
152the method due to the inheritance. We can also choose to override the default
153implementation of the `summary` method when we implement the `Summarizable`
154trait, which is similar to a child class overriding the implementation of a
155method inherited from a parent class.
156
157The second reason to use inheritance is with the type system: to express that a
158child type can be used in the same places that the parent type can be used.
159This is also called *polymorphism*, which means that multiple objects can be
160substituted for each other at runtime if they have the same shape.
161
162<!-- PROD: START BOX -->
163
3b2f2976
XL
164> While many people use “polymorphism” to describe inheritance, it’s actually
165> a specific kind of polymorphism, called “sub-type polymorphism.” There are
cc61c64b 166> other forms as well; a generic parameter with a trait bound in Rust is
3b2f2976
XL
167> also polymorphism, more specifically “parametric polymorphism.” The exact
168> details between the different kinds of polymorphism aren’t crucial here,
169> so don’t worry too much about the details: just know that Rust has multiple
cc61c64b
XL
170> polymorphism-related features, unlike many OOP languages.
171
172<!-- PROD: END BOX -->
173
174To support this sort of pattern, Rust has *trait objects* so that we can
175specify that we would like values of any type, as long as the values implement
176a particular trait.
177
178Inheritance has recently fallen out of favor as a programming design solution
179in many programming languages. Using inheritance to re-use some code can
3b2f2976 180require more code to be shared than you actually need. Subclasses shouldn’t
cc61c64b 181always share all characteristics of their parent class, but inheritance means
3b2f2976
XL
182the subclass gets all of its parent’s data and behavior. This can make a
183program’s design less flexible, and creates the possibility of calling methods
184on subclasses that don’t make sense or cause errors since the methods don’t
cc61c64b
XL
185apply to the subclass but must be inherited from the parent class. In addition,
186some languages only allow a subclass to inherit from one class, further
3b2f2976 187restricting the flexibility of a program’s design.
cc61c64b
XL
188
189For these reasons, Rust chose to take a different approach with trait objects
3b2f2976 190instead of inheritance. Let’s take a look at how trait objects enable
cc61c64b 191polymorphism in Rust.