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