]> git.proxmox.com Git - rustc.git/blobdiff - 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
index 9a3c7b461aeabebbf013969ec8e4f2fe50a0b3bf..fe8cdeac2bbc50616db64ac3cc2563f45c911da7 100644 (file)
@@ -1,15 +1,23 @@
 ## What Does Object-Oriented Mean?
 
-There isn’t consensus in the programming community about the features a
-language needs to have in order to be called object-oriented. Rust is
-influenced by many different programming paradigms; we explored the features it
-has that come from functional programming in Chapter 13. Some of the
-characteristics that object-oriented programming languages tend to share are
-objects, encapsulation, and inheritance. Let’s take a look at what each of
-those mean and whether Rust supports them.
+There’s no consensus in the programming community about what features a
+language needs in order to be called object-oriented. Rust is influenced by
+many different programming paradigms including OOP; we explored, for example,
+the features that came from functional programming in Chapter 13. Arguably,
+object-oriented programming languages do tend to share certain common
+characteristics, namely objects, encapsulation, and inheritance. Let’s take a
+look at what each of those mean and whether Rust supports them.
 
 ### Objects Contain Data and Behavior
 
+<!-- Is there a reason we're using this book as the reference, is it generally
+accepted as an authority? -->
+<!-- Yes, it is. For example, Martin Fowler (himself regarded as an authority)
+had this to say about it https://www.martinfowler.com/bliki/GangOfFour.html:
+> In my view the Gang of Four is the best book ever written on object-oriented
+> design - possibly of any style of design.
+/Carol -->
+
 The book “Design Patterns: Elements of Reusable Object-Oriented Software,”
 colloquially referred to as “The Gang of Four book,” is a catalog of
 object-oriented design patterns. It defines object-oriented programming in this
@@ -22,27 +30,27 @@ way:
 Under this definition, then, Rust is object-oriented: structs and enums have
 data and `impl` blocks provide methods on structs and enums. Even though
 structs and enums with methods aren’t *called* objects, they provide the same
-functionality that objects do, using the Gang of Four’s definition of objects.
+functionality, under the Gang of Four’s definition of objects.
 
 ### Encapsulation that Hides Implementation Details
 
 Another aspect commonly associated with object-oriented programming is the idea
-of *encapsulation*: the implementation details of an object aren’t accessible
-to code using that object. The only way to interact with an object is through
-the public API the object offers; code using the object should not be able to
-reach into the object’s internals and change data or behavior directly.
-Encapsulation enables changing and refactoring an object’s internals without
+of *encapsulation*: that the implementation details of an object aren’t
+accessible to code using that object. The only way to interact with an object
+therefore is through its public API; code using the object should not be able
+to reach into the object’s internals and change data or behavior directly. This
+enables the programmer to change and refactor an object’s internals without
 needing to change the code that uses the object.
 
-As we discussed in Chapter 7, we can use the `pub` keyword to decide what
-modules, types, functions, and methods in our code should be public, and by
-default, everything is private. For example, we can define a struct
-`AveragedCollection` that has a field containing a vector of `i32` values. The
-struct can also have a field that knows the average of the values in the vector
-so that whenever anyone wants to know the average of the values that the struct
-has in its vector, we don’t have to compute it on-demand. `AveragedCollection`
-will cache the calculated average for us. Listing 17-1 has the definition of
-the `AveragedCollection` struct:
+We discussed an example of this in Chapter 7: We can use the `pub` keyword to
+decide what modules, types, functions, and methods in our code should be
+public, and by default everything else is private. For example, we can define a
+struct `AveragedCollection` that has a field containing a vector of `i32`
+values. The struct can also have a field that contains the average of the
+values in the vector, meaning the average doesn’t have to be computed on-demand
+whenever anyone needs it. In other words, `AveragedCollection` will cache the
+calculated average for us. Listing 17-1 has the definition of the
+`AveragedCollection` struct:
 
 <span class="filename">Filename: src/lib.rs</span>
 
@@ -57,11 +65,11 @@ pub struct AveragedCollection {
 maintains a list of integers and the average of the items in the
 collection.</span>
 
-Note that the struct itself is marked `pub` so that other code may use this
-struct, but the fields within the struct remain private. This is important in
-this case because we want to ensure that whenever a value is added or removed
-from the list, we also update the average. We do this by implementing `add`,
-`remove`, and `average` methods on the struct as shown in Listing 17-2:
+The struct itself is marked `pub` so that other code may use it, but the fields
+within the struct remain private. This is important in this case because we
+want to ensure that whenever a value is added or removed from the list, the
+average is also updated. We do this by implementing `add`, `remove`, and
+`average` methods on the struct as shown in Listing 17-2:
 
 <span class="filename">Filename: src/lib.rs</span>
 
@@ -102,90 +110,87 @@ impl AveragedCollection {
 `add`, `remove`, and `average` on `AveragedCollection`</span>
 
 The public methods `add`, `remove`, and `average` are the only way to modify an
-instance of a `AveragedCollection`. When an item is added to `list` using the
-`add` method or removed using the `remove` method, the implementations of those
-methods call the private `update_average` method that takes care of updating
-the `average` field as well. Because the `list` and `average` fields are
-private, there’s no way for external code to add or remove items to the `list`
-field directly, which could cause the `average` field to get out of sync. The
-`average` method returns the value in the `average` field, which allows
-external code to read the `average` but not modify it.
+instance of `AveragedCollection`. When an item is added to `list` using the
+`add` method or removed using the `remove` method, the implementations of each
+call the private `update_average` method that takes care of updating the
+`average` field as well.
+
+We leave the `list` and `average` fields private so that there’s no way for
+external code to add or remove items to the `list` field directly, otherwise
+the `average` field might become out of sync when the `list` changes. The
+`average` method returns the value in the `average` field, allowing external
+code to read the `average` but not modify it.
 
 Because we’ve encapsulated the implementation details of `AveragedCollection`,
 we can easily change aspects like the data structure in the future. For
 instance, we could use a `HashSet` instead of a `Vec` for the `list` field. As
 long as the signatures of the `add`, `remove`, and `average` public methods
-stay the same, code using `AveragedCollection` wouldn’t need to change. This
-wouldn’t necessarily be the case if we exposed `list` to external code:
-`HashSet` and `Vec` have different methods for adding and removing items, so
-the external code would likely have to change if it was modifying `list`
-directly.
+stay the same, code using `AveragedCollection` wouldn’t need to change. If we
+made `list` public instead, this wouldn’t necessarily be the case: `HashSet`
+and `Vec` have different methods for adding and removing items, so the external
+code would likely have to change if it was modifying `list` directly.
 
 If encapsulation is a required aspect for a language to be considered
-object-oriented, then Rust meets that requirement. Using `pub` or not for
-different parts of code enables encapsulation of implementation details.
+object-oriented, then Rust meets that requirement. The option to use `pub` or
+not for different parts of code enables encapsulation of implementation details.
 
 ### Inheritance as a Type System and as Code Sharing
 
-*Inheritance* is a mechanism that some programming languages provide whereby an
-object can be defined to inherit from another object’s definition, thus gaining
-the parent object’s data and behavior without having to define those again.
-Inheritance is a characteristic that is part of some people’s definitions of
-what an OOP language is.
+*Inheritance* is a mechanism whereby an object can inherit from another
+object’s definition, thus gaining the parent object’s data and behavior without
+you having to define them again.
 
 If a language must have inheritance to be an object-oriented language, then
-Rust is not object-oriented. There is not a way to define a struct that
-inherits from another struct in order to gain the parent struct’s fields and
-method implementations. However, if you’re used to having inheritance in your
-programming toolbox, there are other solutions in Rust depending on the reason
-you want to use inheritance.
-
-There are two main reasons to reach for inheritance. The first is to be able to
-re-use code: once a particular behavior is implemented for one type,
-inheritance can enable re-using that implementation for a different type. Rust
-code can be shared using default trait method implementations instead, which we
-saw in Listing 10-15 when we added a default implementation of the `summary`
-method on the `Summarizable` trait. Any type implementing the `Summarizable`
-trait would have the `summary` method available on it without any further code.
-This is similar to a parent class having an implementation of a method, and a
-child class inheriting from the parent class also having the implementation of
-the method due to the inheritance. We can also choose to override the default
-implementation of the `summary` method when we implement the `Summarizable`
-trait, which is similar to a child class overriding the implementation of a
-method inherited from a parent class.
-
-The second reason to use inheritance is with the type system: to express that a
-child type can be used in the same places that the parent type can be used.
-This is also called *polymorphism*, which means that multiple objects can be
-substituted for each other at runtime if they have the same shape.
+Rust is not. There is no way to define a struct that inherits the parent
+struct’s fields and method implementations. However, if you’re used to having
+inheritance in your programming toolbox, there are other solutions in Rust
+depending on your reason for reaching for inheritance in the first place.
+
+There are two main reasons to choose inheritance. The first is for re-use of
+code: you can implement particular behavior for one type, and inheritance
+enables you to re-use that implementation for a different type. Rust code can
+be shared using default trait method implementations instead, which we saw in
+Listing 10-15 when we added a default implementation of the `summary` method on
+the `Summarizable` trait. Any type implementing the `Summarizable` trait would
+have the `summary` method available on it without any further code. This is
+similar to a parent class having an implementation of a method, and an
+inheriting child class then also having the implementation of the method. We
+can also choose to override the default implementation of the `summary` method
+when we implement the `Summarizable` trait, similar to a child class overriding
+the implementation of a method inherited from a parent class.
+
+The second reason to use inheritance relates to the type system: to enable a
+child type to be used in the same places as the parent type. This is also
+called *polymorphism*, which means that multiple objects can be substituted for
+each other at runtime if they share certain characteristics.
+
+<!-- What does it mean for objects to have the same shape? -->
+<!-- The use of "shape" in this context has to do with the roots of "morph" in
+"polymorphism", but it's not very well defined so I've reworded. /Carol -->
 
 <!-- PROD: START BOX -->
 
-> While many people use “polymorphism” to describe inheritance, it’s actually
-> a specific kind of polymorphism, called “sub-type polymorphism.” There are
-> other forms as well; a generic parameter with a trait bound in Rust is
-> also polymorphism, more specifically “parametric polymorphism.” The exact
-> details between the different kinds of polymorphism aren’t crucial here,
-> so don’t worry too much about the details: just know that Rust has multiple
-> polymorphism-related features, unlike many OOP languages.
+> Polymorphism
+>
+> To many people, polymorphism is synonymous with inheritance. But it’s
+> actually a more general concept that refers to code that can work with data
+> of multiple types. For inheritance, those types are generally subclasses.
+> Rust instead uses generics to abstract over different possible types, and
+> trait bounds to impose constraints on what those types must provide. This is
+> sometimes called *bounded parametric polymorphism*.
 
 <!-- PROD: END BOX -->
 
-To support this sort of pattern, Rust has *trait objects* so that we can
-specify that we would like values of any type, as long as the values implement
-a particular trait.
-
 Inheritance has recently fallen out of favor as a programming design solution
-in many programming languages. Using inheritance to re-use some code can
-require more code to be shared than you actually need. Subclasses shouldn’t
-always share all characteristics of their parent class, but inheritance means
-the subclass gets all of its parent’s data and behavior. This can make a
-program’s design less flexible, and creates the possibility of calling methods
-on subclasses that don’t make sense or cause errors since the methods don’t
-apply to the subclass but must be inherited from the parent class. In addition,
-some languages only allow a subclass to inherit from one class, further
-restricting the flexibility of a program’s design.
-
-For these reasons, Rust chose to take a different approach with trait objects
+in many programming languages because it’s often at risk of sharing more code
+than needs be. Subclasses shouldn’t always share all characteristics of their
+parent class, but will do so with inheritance. This can make a program’s design
+less flexible, and introduces the possibility of calling methods on subclasses
+that don’t make sense or that cause errors because the methods don’t actually
+apply to the subclass. Some languages will also only allow a subclass to
+inherit from one class, further restricting the flexibility of a program’s
+design.
+
+For these reasons, Rust chose to take a different approach, using trait objects
 instead of inheritance. Let’s take a look at how trait objects enable
 polymorphism in Rust.