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