]> git.proxmox.com Git - rustc.git/blame - src/doc/book/second-edition/src/ch17-01-what-is-oo.md
New upstream version 1.28.0~beta.14+dfsg1
[rustc.git] / src / doc / book / second-edition / src / ch17-01-what-is-oo.md
CommitLineData
83c7162d 1## Characteristics of Object-Oriented Languages
cc61c64b 2
0531ce1d 3There is no consensus in the programming community about what features a
83c7162d
XL
4language must have to be considered object oriented. Rust is influenced by many
5programming paradigms, including OOP; for example, we explored the features
6that came from functional programming in Chapter 13. Arguably, OOP languages
7share certain common characteristics, namely objects, encapsulation, and
8inheritance. Let’s look at what each of those characteristics means and whether
9Rust supports it.
cc61c64b
XL
10
11### Objects Contain Data and Behavior
12
83c7162d
XL
13The book *Design Patterns: Elements of Reusable Object-Oriented Software* by
14Enoch Gamma, Richard Helm, Ralph Johnson, and John Vlissides (Addison-Wesley
15Professional, 1994) colloquially referred to as *The Gang of Four* book, is a
16catalog of object-oriented design patterns. It defines OOP this way:
cc61c64b
XL
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
0531ce1d
XL
22Using this definition, Rust is object oriented: structs and enums have data,
23and `impl` blocks provide methods on structs and enums. Even though structs and
24enums with methods aren’t *called* objects, they provide the same
25functionality, according to the Gang of Four’s definition of objects.
cc61c64b
XL
26
27### Encapsulation that Hides Implementation Details
28
0531ce1d
XL
29Another aspect commonly associated with OOP is the idea of *encapsulation*,
30which means that the implementation details of an object aren’t accessible to
31code using that object. Therefore, the only way to interact with an object is
32through its public API; code using the object shouldn’t be able to reach into
33the object’s internals and change data or behavior directly. This enables the
34programmer to change and refactor an object’s internals without needing to
35change the code that uses the object.
36
37We discussed how to control encapsulation in Chapter 7: we can use the `pub`
38keyword to decide which modules, types, functions, and methods in our code
39should be public, and by default everything else is private. For example, we
40can define a struct `AveragedCollection` that has a field containing a vector
41of `i32` values. The struct can also have a field that contains the average of
42the values in the vector, meaning the average doesn’t have to be computed
83c7162d 43on demand whenever anyone needs it. In other words, `AveragedCollection` will
0531ce1d 44cache the calculated average for us. Listing 17-1 has the definition of the
abe05a73 45`AveragedCollection` struct:
cc61c64b
XL
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
0531ce1d 58collection</span>
cc61c64b 59
0531ce1d
XL
60The struct is marked `pub` so that other code can use it, but the fields within
61the struct remain private. This is important in this case because we want to
62ensure that whenever a value is added or removed from the list, the average is
63also updated. We do this by implementing `add`, `remove`, and `average` methods
64on the struct, as shown in Listing 17-2:
cc61c64b
XL
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
0531ce1d
XL
104The public methods `add`, `remove`, and `average` are the only ways to modify
105an instance of `AveragedCollection`. When an item is added to `list` using the
abe05a73 106`add` method or removed using the `remove` method, the implementations of each
0531ce1d
XL
107call the private `update_average` method that handles updating the `average`
108field as well.
abe05a73 109
0531ce1d
XL
110We leave the `list` and `average` fields private so there is no way for
111external code to add or remove items to the `list` field directly; otherwise,
abe05a73
XL
112the `average` field might become out of sync when the `list` changes. The
113`average` method returns the value in the `average` field, allowing external
114code to read the `average` but not modify it.
cc61c64b 115
83c7162d
XL
116Because we’ve encapsulated the implementation details of the struct
117`AveragedCollection`, we can easily change aspects, such as the data structure,
94b46f34
XL
118in the future. For instance, we could use a `HashSet<i32>` instead of a
119`Vec<i32>` for the `list` field. As long as the signatures of the `add`,
120`remove`, and `average` public methods stay the same, code using
121`AveragedCollection` wouldn’t need to change. If we made `list` public instead,
122this wouldn’t necessarily be the case: `HashSet<i32>` and `Vec<i32>` have
123different methods for adding and removing items, so the external code would
124likely have to change if it were modifying `list` directly.
cc61c64b 125
0531ce1d
XL
126If encapsulation is a required aspect for a language to be considered object
127oriented, then Rust meets that requirement. The option to use `pub` or not for
128different parts of code enables encapsulation of implementation details.
cc61c64b
XL
129
130### Inheritance as a Type System and as Code Sharing
131
abe05a73
XL
132*Inheritance* is a mechanism whereby an object can inherit from another
133object’s definition, thus gaining the parent object’s data and behavior without
134you having to define them again.
cc61c64b
XL
135
136If a language must have inheritance to be an object-oriented language, then
83c7162d 137Rust is not one. There is no way to define a struct that inherits the parent
abe05a73 138struct’s fields and method implementations. However, if you’re used to having
83c7162d 139inheritance in your programming toolbox, you can use other solutions in Rust,
abe05a73
XL
140depending on your reason for reaching for inheritance in the first place.
141
0531ce1d
XL
142You choose inheritance for two main reasons. One is for reuse of code: you can
143implement particular behavior for one type, and inheritance enables you to
144reuse that implementation for a different type. You can share Rust code using
145default trait method implementations instead, which you saw in Listing 10-14
146when we added a default implementation of the `summarize` method on the
147`Summary` trait. Any type implementing the `Summary` trait would have the
148`summarize` method available on it without any further code. This is similar to
149a parent class having an implementation of a method and an inheriting child
150class also having the implementation of the method. We can also override the
151default implementation of the `summarize` method when we implement the
152`Summary` trait, which is similar to a child class overriding the
153implementation of a method inherited from a parent class.
154
155The other reason to use inheritance relates to the type system: to enable a
abe05a73 156child type to be used in the same places as the parent type. This is also
0531ce1d 157called *polymorphism*, which means that you can substitute multiple objects for
abe05a73
XL
158each other at runtime if they share certain characteristics.
159
0531ce1d 160> ### Polymorphism
abe05a73
XL
161>
162> To many people, polymorphism is synonymous with inheritance. But it’s
163> actually a more general concept that refers to code that can work with data
164> of multiple types. For inheritance, those types are generally subclasses.
0531ce1d
XL
165>
166> Rust instead uses generics to abstract over different possible types and
abe05a73
XL
167> trait bounds to impose constraints on what those types must provide. This is
168> sometimes called *bounded parametric polymorphism*.
cc61c64b 169
cc61c64b 170Inheritance has recently fallen out of favor as a programming design solution
abe05a73 171in many programming languages because it’s often at risk of sharing more code
83c7162d 172than necessary. Subclasses shouldn’t always share all characteristics of their
0531ce1d 173parent class but will do so with inheritance. This can make a program’s design
83c7162d
XL
174less flexible. It also introduces the possibility of calling methods on
175subclasses that don’t make sense or that cause errors because the methods don’t
176apply to the subclass. In addition, some languages will only allow a subclass
177to inherit from one class, further restricting the flexibility of a program’s
178design.
0531ce1d
XL
179
180For these reasons, Rust takes a different approach, using trait objects instead
181of inheritance. Let’s look at how trait objects enable polymorphism in Rust.