You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: rustbook-en/src/ch18-01-what-is-oo.md
+25-25Lines changed: 25 additions & 25 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -39,10 +39,10 @@ keyword to decide which modules, types, functions, and methods in our code
39
39
should be public, and by default everything else is private. For example, we
40
40
can define a struct `AveragedCollection` that has a field containing a vector
41
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
42
+
the values in the vector, meaning the average doesn’t have to be computed on
43
+
demand whenever anyone needs it. In other words, `AveragedCollection` will
44
44
cache the calculated average for us. Listing 18-1 has the definition of the
45
-
`AveragedCollection` struct:
45
+
`AveragedCollection` struct.
46
46
47
47
<Listingnumber="18-1"file-name="src/lib.rs"caption="An `AveragedCollection` struct that maintains a list of integers and the average of the items in the collection">
48
48
@@ -56,7 +56,7 @@ The struct is marked `pub` so that other code can use it, but the fields within
56
56
the struct remain private. This is important in this case because we want to
57
57
ensure that whenever a value is added or removed from the list, the average is
58
58
also updated. We do this by implementing `add`, `remove`, and `average` methods
59
-
on the struct, as shown in Listing 18-2:
59
+
on the struct, as shown in Listing 18-2.
60
60
61
61
<Listingnumber="18-2"file-name="src/lib.rs"caption="Implementations of the public methods `add`, `remove`, and `average` on `AveragedCollection`">
62
62
@@ -67,8 +67,8 @@ on the struct, as shown in Listing 18-2:
67
67
</Listing>
68
68
69
69
The public methods `add`, `remove`, and `average` are the only ways to access
70
-
or modify data in an instance of `AveragedCollection`. When an item is added
71
-
to `list` using the `add` method or removed using the `remove` method, the
70
+
or modify data in an instance of `AveragedCollection`. When an item is added to
71
+
`list` using the `add` method or removed using the `remove` method, the
72
72
implementations of each call the private `update_average` method that handles
73
73
updating the `average` field as well.
74
74
@@ -88,19 +88,19 @@ this wouldn’t necessarily be the case: `HashSet<i32>` and `Vec<i32>` have
88
88
different methods for adding and removing items, so the external code would
89
89
likely have to change if it were modifying `list` directly.
90
90
91
-
If encapsulation is a required aspect for a language to be considered
92
-
object oriented, then Rust meets that requirement. The option to use `pub` or
93
-
not for different parts of code enables encapsulation of implementation details.
91
+
If encapsulation is a required aspect for a language to be considered object
92
+
oriented, then Rust meets that requirement. The option to use `pub` or not for
93
+
different parts of code enables encapsulation of implementation details.
94
94
95
95
### Inheritance as a Type System and as Code Sharing
96
96
97
97
_Inheritance_ is a mechanism whereby an object can inherit elements from
98
98
another object’s definition, thus gaining the parent object’s data and behavior
99
99
without you having to define them again.
100
100
101
-
If a language must have inheritance to be object oriented, then Rust is not such
102
-
a language. There is no way to define a struct that inherits the parent struct’s
103
-
fields and method implementations without using a macro.
101
+
If a language must have inheritance to be object oriented, then Rust is not
102
+
such a language. There is no way to define a struct that inherits the parent
103
+
struct’s fields and method implementations without using a macro.
104
104
105
105
However, if you’re used to having inheritance in your programming toolbox, you
106
106
can use other solutions in Rust, depending on your reason for reaching for
@@ -127,23 +127,23 @@ each other at runtime if they share certain characteristics.
127
127
> ### Polymorphism
128
128
>
129
129
> To many people, polymorphism is synonymous with inheritance. But it’s
130
-
> actually a more general concept that refers to code that can work with data
131
-
> of multiple types. For inheritance, those types are generally subclasses.
130
+
> actually a more general concept that refers to code that can work with data of
131
+
> multiple types. For inheritance, those types are generally subclasses.
132
132
>
133
133
> Rust instead uses generics to abstract over different possible types and
134
134
> trait bounds to impose constraints on what those types must provide. This is
135
135
> sometimes called _bounded parametric polymorphism_.
136
136
137
-
Inheritance has recently fallen out of favor as a programming design solution in
138
-
many programming languages because it’s often at risk of sharing more code than
139
-
necessary. Subclasses shouldn’t always share all characteristics of their parent
140
-
class but will do so with inheritance. This can make a program’s design less
141
-
flexible. It also introduces the possibility of calling methods on subclasses
142
-
that don’t make sense or that cause errors because the methods don’t apply to
143
-
the subclass. In addition, some languages will only allow single inheritance
144
-
(meaning a subclass can only inherit from one class), further restricting the
145
-
flexibility of a program’s design.
137
+
Rust has chosen a different set of tradeoffs by not offering inheritance.
138
+
Inheritance is often at risk of sharing more code than necessary. Subclasses
139
+
shouldn’t always share all characteristics of their parent class but will do so
140
+
with inheritance. This can make a program’s design less flexible. It also
141
+
introduces the possibility of calling methods on subclasses that don’t make
142
+
sense or that cause errors because the methods don’t apply to the subclass. In
143
+
addition, some languages will only allow _single inheritance_ (meaning a
144
+
subclass can only inherit from one class), further restricting the flexibility
145
+
of a program’s design.
146
146
147
147
For these reasons, Rust takes the different approach of using trait objects
148
-
instead of inheritance. Let’s look at how trait objects enable polymorphism in
149
-
Rust.
148
+
instead of inheritance to enable polymorphism. Let’s look at how trait objects
In Chapter 8, we mentioned that one limitation of vectors is that they can
4
8
store elements of only one type. We created a workaround in Listing 8-9 where
@@ -19,13 +23,12 @@ some types for people to use, such as `Button` or `TextField`. In addition,
19
23
instance, one programmer might add an `Image` and another might add a
20
24
`SelectBox`.
21
25
22
-
We won’t implement a full-fledged GUI library for this example but will show
23
-
how the pieces would fit together. At the time of writing the library, we can’t
24
-
know and define all the types other programmers might want to create. But we do
25
-
know that `gui` needs to keep track of many values of different types, and it
26
-
needs to call a `draw` method on each of these differently typed values. It
27
-
doesn’t need to know exactly what will happen when we call the `draw` method,
28
-
just that the value will have that method available for us to call.
26
+
At the time of writing the library, we can’t know and define all the types
27
+
other programmers might want to create. But we do know that `gui` needs to keep
28
+
track of many values of different types, and it needs to call a `draw` method
29
+
on each of these differently typed values. It doesn’t need to know exactly what
30
+
will happen when we call the `draw` method, just that the value will have that
31
+
method available for us to call.
29
32
30
33
To do this in a language with inheritance, we might define a class named
31
34
`Component` that has a method named `draw` on it. The other classes, such as
@@ -34,7 +37,7 @@ inherit the `draw` method. They could each override the `draw` method to define
34
37
their custom behavior, but the framework could treat all of the types as if
35
38
they were `Component` instances and call `draw` on them. But because Rust
36
39
doesn’t have inheritance, we need another way to structure the `gui` library to
37
-
allow users to extend it with new types.
40
+
allow users to create new types compatible with the library.
38
41
39
42
### Defining a Trait for Common Behavior
40
43
@@ -56,12 +59,10 @@ We’ve mentioned that, in Rust, we refrain from calling structs and enums
56
59
“objects” to distinguish them from other languages’ objects. In a struct or
57
60
enum, the data in the struct fields and the behavior in `impl` blocks are
58
61
separated, whereas in other languages, the data and behavior combined into one
59
-
concept is often labeled an object. However, trait objects _are_ more like
60
-
objects in other languages in the sense that they combine data and behavior.
61
-
But trait objects differ from traditional objects in that we can’t add data to
62
-
a trait object. Trait objects aren’t as generally useful as objects in other
63
-
languages: their specific purpose is to allow abstraction across common
64
-
behavior.
62
+
concept is often labeled an object. Trait objects differ from objects in other
63
+
languages in that we can’t add data to a trait object. Trait objects aren’t as
64
+
generally useful as objects in other languages: their specific purpose is to
65
+
allow abstraction across common behavior.
65
66
66
67
Listing 18-3 shows how to define a trait named `Draw` with one method named
67
68
`draw`.
@@ -77,8 +78,8 @@ Listing 18-3 shows how to define a trait named `Draw` with one method named
77
78
This syntax should look familiar from our discussions on how to define traits
78
79
in Chapter 10. Next comes some new syntax: Listing 18-4 defines a struct named
79
80
`Screen` that holds a vector named `components`. This vector is of type
80
-
`Box<dyn Draw>`, which is a trait object; it’s a stand-in for any type inside
81
-
a `Box` that implements the `Draw` trait.
81
+
`Box<dyn Draw>`, which is a trait object; it’s a stand-in for any type inside a
82
+
`Box` that implements the `Draw` trait.
82
83
83
84
<Listingnumber="18-4"file-name="src/lib.rs"caption="Definition of the `Screen` struct with a `components` field holding a vector of trait objects that implement the `Draw` trait">
84
85
@@ -103,8 +104,8 @@ This works differently from defining a struct that uses a generic type
103
104
parameter with trait bounds. A generic type parameter can be substituted with
104
105
only one concrete type at a time, whereas trait objects allow for multiple
105
106
concrete types to fill in for the trait object at runtime. For example, we
106
-
could have defined the `Screen` struct using a generic type and a trait bound
107
-
as in Listing 18-6:
107
+
could have defined the `Screen` struct using a generic type and a trait bound,
108
+
as in Listing 18-6.
108
109
109
110
<Listingnumber="18-6"file-name="src/lib.rs"caption="An alternate implementation of the `Screen` struct and its `run` method using generics and trait bounds">
110
111
@@ -130,7 +131,7 @@ Now we’ll add some types that implement the `Draw` trait. We’ll provide the
130
131
`Button` type. Again, actually implementing a GUI library is beyond the scope
131
132
of this book, so the `draw` method won’t have any useful implementation in its
132
133
body. To imagine what the implementation might look like, a `Button` struct
133
-
might have fields for `width`, `height`, and `label`, as shown in Listing 18-7:
134
+
might have fields for `width`, `height`, and `label`, as shown in Listing 18-7.
134
135
135
136
<Listingnumber="18-7"file-name="src/lib.rs"caption="A `Button` struct that implements the `Draw` trait">
136
137
@@ -166,7 +167,7 @@ Our library’s user can now write their `main` function to create a `Screen`
166
167
instance. To the `Screen` instance, they can add a `SelectBox` and a `Button`
167
168
by putting each in a `Box<T>` to become a trait object. They can then call the
168
169
`run` method on the `Screen` instance, which will call `draw` on each of the
169
-
components. Listing 18-9 shows this implementation:
170
+
components. Listing 18-9 shows this implementation.
170
171
171
172
<Listingnumber="18-9"file-name="src/main.rs"caption="Using trait objects to store values of different types that implement the same trait">
172
173
@@ -183,9 +184,9 @@ means it implements the `draw` method.
183
184
184
185
This concept—of being concerned only with the messages a value responds to
185
186
rather than the value’s concrete type—is similar to the concept of _duck
186
-
typing_ in dynamically typed languages: if it walks like a duck and quacks
187
-
like a duck, then it must be a duck! In the implementation of `run` on `Screen`
188
-
in Listing 18-5, `run` doesn’t need to know what the concrete type of each
187
+
typing_ in dynamically typed languages: if it walks like a duck and quacks like
188
+
a duck, then it must be a duck! In the implementation of `run` on `Screen` in
189
+
Listing 18-5, `run` doesn’t need to know what the concrete type of each
189
190
component is. It doesn’t check whether a component is an instance of a `Button`
190
191
or a `SelectBox`, it just calls the `draw` method on the component. By
191
192
specifying `Box<dyn Draw>` as the type of the values in the `components`
@@ -225,25 +226,25 @@ Recall in [“Performance of Code Using
225
226
Generics”][performance-of-code-using-generics]<!-- ignore --> in Chapter 10 our
226
227
discussion on the monomorphization process performed on generics by the
227
228
compiler: the compiler generates nongeneric implementations of functions and
228
-
methods for each concrete type that we use in place of a generic type parameter.
229
-
The code that results from monomorphization is doing _static dispatch_, which is
230
-
when the compiler knows what method you’re calling at compile time. This is
231
-
opposed to _dynamic dispatch_, which is when the compiler can’t tell at compile
232
-
time which method you’re calling. In dynamic dispatch cases, the compiler emits
233
-
code that at runtime will figure out which method to call.
229
+
methods for each concrete type that we use in place of a generic type
230
+
parameter. The code that results from monomorphization is doing _static
231
+
dispatch_, which is when the compiler knows what method you’re calling at
232
+
compile time. This is opposed to _dynamic dispatch_, which is when the compiler
233
+
can’t tell at compile time which method you’re calling. In dynamic dispatch
234
+
cases, the compiler emits code that at runtime will know which method to call.
234
235
235
236
When we use trait objects, Rust must use dynamic dispatch. The compiler doesn’t
236
237
know all the types that might be used with the code that’s using trait objects,
237
238
so it doesn’t know which method implemented on which type to call. Instead, at
238
239
runtime, Rust uses the pointers inside the trait object to know which method to
239
240
call. This lookup incurs a runtime cost that doesn’t occur with static dispatch.
240
241
Dynamic dispatch also prevents the compiler from choosing to inline a method’s
241
-
code, which in turn prevents some optimizations, and Rust has some rules, called
242
-
_dyn compatibility_, about where you can and cannot use dynamic dispatch. Those
243
-
rules are beyond the scope of this discussion, but you can read more about them
244
-
[in the reference][dyn-compatibility]. However, we did get extra flexibility in
245
-
the code that we wrote in Listing 18-5 and were able to support in Listing 18-9,
246
-
so it’s a trade-off to consider.
242
+
code, which in turn prevents some optimizations, and Rust has some rules about
243
+
where you can and cannot use dynamic dispatch, called _dyn compatibility_. Those
244
+
rules are beyond the scope of this discussion, but you can read more about them
245
+
[in the reference][dyn-compatibility]<!-- ignore -->. However, we did get extra
246
+
flexibility in the code that we wrote in Listing 18-5 and were able to support
0 commit comments