11## Using Trait Objects that Allow for Values of Different Types
22
3- In Chapter 8, we mentioned that one limitation of vectors is that they can only
4- store elements of one type. We created a workaround in Listing 8-10 where we
5- defined a ` SpreadsheetCell ` enum that had variants to hold integers, floats,
3+ In Chapter 8, we mentioned that one limitation of vectors is that they can
4+ store elements of only one type. We created a workaround in Listing 8-10 where
5+ we defined a ` SpreadsheetCell ` enum that had variants to hold integers, floats,
66and text. This meant we could store different types of data in each cell and
77still have a vector that represented a row of cells. This is a perfectly good
88solution when our interchangeable items are a fixed set of types that we know
@@ -43,17 +43,17 @@ To implement the behavior we want `gui` to have, we’ll define a trait named
4343takes a * trait object* . A trait object points to an instance of a type that
4444implements the trait we specify. We create a trait object by specifying some
4545sort of pointer, such as a ` & ` reference or a ` Box<T> ` smart pointer, and then
46- specifying the relevant trait (we ’ll talk about the reason trait objects must
47- use a pointer in Chapter 19 in the section “Dynamically Sized Types & Sized”).
46+ specifying the relevant trait. (We ’ll talk about the reason trait objects must
47+ use a pointer in Chapter 19 in the section “Dynamically Sized Types & Sized”.)
4848We can use trait objects in place of a generic or concrete type. Wherever we
4949use a trait object, Rust’s type system will ensure at compile time that any
5050value used in that context will implement the trait object’s trait.
5151Consequently, we don’t need to know all the possible types at compile time.
5252
53- We’ve mentioned that in Rust we refrain from calling structs and enums
53+ We’ve mentioned that in Rust, we refrain from calling structs and enums
5454“objects” to distinguish them from other languages’ objects. In a struct or
5555enum, the data in the struct fields and the behavior in ` impl ` blocks are
56- separated, whereas in other languages the data and behavior combined into one
56+ separated, whereas in other languages, the data and behavior combined into one
5757concept is often labeled an object. However, trait objects * are* more like
5858objects in other languages in the sense that they combine data and behavior.
5959But trait objects differ from traditional objects in that we can’t add data to
@@ -77,7 +77,7 @@ pub trait Draw {
7777This syntax should look familiar from our discussions on how to define traits
7878in Chapter 10. Next comes some new syntax: Listing 17-4 defines a struct named
7979` Screen ` that holds a vector named ` components ` . This vector is of type
80- ` Box<Draw> ` , which is a trait object: it’s a stand-in for any type inside a
80+ ` Box<Draw> ` , which is a trait object; it’s a stand-in for any type inside a
8181` Box ` that implements the ` Draw ` trait.
8282
8383<span class =" filename " >Filename: src/lib.rs</span >
@@ -119,8 +119,8 @@ impl Screen {
119119}
120120```
121121
122- <span class =" caption " >Listing 17-5: Implementing a ` run ` method on ` Screen `
123- that calls the ` draw ` method on each component</span >
122+ <span class =" caption " >Listing 17-5: A ` run ` method on ` Screen ` that calls the
123+ ` draw ` method on each component</span >
124124
125125This works differently than defining a struct that uses a generic type
126126parameter with trait bounds. A generic type parameter can only be substituted
@@ -186,7 +186,7 @@ pub struct Button {
186186
187187impl Draw for Button {
188188 fn draw (& self ) {
189- // Code to actually draw a button
189+ // code to actually draw a button
190190 }
191191}
192192```
@@ -198,9 +198,9 @@ The `width`, `height`, and `label` fields on `Button` will differ from the
198198fields on other components, such as a ` TextField ` type, that might have those
199199fields plus a ` placeholder ` field instead. Each of the types we want to draw on
200200the screen will implement the ` Draw ` trait but will use different code in the
201- ` draw ` method to define how to draw that particular type, like ` Button ` has
202- here (without the actual GUI code that is beyond the scope of this chapter).
203- ` Button ` , for instance, might have an additional ` impl ` block containing
201+ ` draw ` method to define how to draw that particular type, as ` Button ` has here
202+ (without the actual GUI code, which is beyond the scope of this chapter). The
203+ ` Button ` type , for instance, might have an additional ` impl ` block containing
204204methods related to what happens when a user clicks the button. These kinds of
205205methods won’t apply to types like ` TextField ` .
206206
@@ -222,7 +222,7 @@ struct SelectBox {
222222
223223impl Draw for SelectBox {
224224 fn draw(&self) {
225- // Code to actually draw a select box
225+ // code to actually draw a select box
226226 }
227227}
228228```
@@ -284,10 +284,10 @@ It doesn’t check whether a component is an instance of a `Button` or a
284284` Screen ` to need values that we can call the ` draw ` method on.
285285
286286The advantage of using trait objects and Rust’s type system to write code
287- similar to code using duck typing is that we never have to check that a value
288- implements a particular method at runtime or worry about getting errors if a
289- value doesn’t implement a method, but we call it anyway. Rust won’t compile our
290- code if the values don’t implement the traits that the trait objects need.
287+ similar to code using duck typing is that we never have to check whether a
288+ value implements a particular method at runtime or worry about getting errors
289+ if a value doesn’t implement a method but we call it anyway. Rust won’t compile
290+ our code if the values don’t implement the traits that the trait objects need.
291291
292292For example, Listing 17-10 shows what happens if we try to create a ` Screen `
293293with a ` String ` as a component:
@@ -326,36 +326,36 @@ error[E0277]: the trait bound `std::string::String: gui::Draw` is not satisfied
326326```
327327
328328This error lets us know that either we’re passing something to ` Screen ` we
329- didn’t mean to pass and we should pass a different type, or we should implement
329+ didn’t mean to pass and we should pass a different type or we should implement
330330` Draw ` on ` String ` so that ` Screen ` is able to call ` draw ` on it.
331331
332332### Trait Objects Perform Dynamic Dispatch
333333
334334Recall in the “Performance of Code Using Generics” section in Chapter 10 our
335335discussion on the monomorphization process performed by the compiler when we
336- use trait bounds on generics: the compiler generates non-generic
337- implementations of functions and methods for each concrete type that we use in
338- place of a generic type parameter. The code that results from monomorphization
339- is doing * static dispatch* , which is when the compiler knows what method you’re
340- calling at compile time. This is opposed to * dynamic dispatch* , which is when
341- the compiler can’t tell at compile time which method you’re calling. In dynamic
336+ use trait bounds on generics: the compiler generates nongeneric implementations
337+ of functions and methods for each concrete type that we use in place of a
338+ generic type parameter. The code that results from monomorphization is doing
339+ * static dispatch* , which is when the compiler knows what method you’re calling
340+ at compile time. This is opposed to * dynamic dispatch* , which is when the
341+ compiler can’t tell at compile time which method you’re calling. In dynamic
342342dispatch cases, the compiler emits code that at runtime will figure out which
343343method to call.
344344
345345When we use trait objects, Rust must use dynamic dispatch. The compiler doesn’t
346346know all the types that might be used with the code that is using trait
347347objects, so it doesn’t know which method implemented on which type to call.
348348Instead, at runtime, Rust uses the pointers inside the trait object to know
349- which specific method to call. There is a runtime cost when this lookup happens
350- that doesn’t occur with static dispatch. Dynamic dispatch also prevents the
351- compiler from choosing to inline a method’s code, which in turn prevents some
349+ which method to call. There is a runtime cost when this lookup happens that
350+ doesn’t occur with static dispatch. Dynamic dispatch also prevents the compiler
351+ from choosing to inline a method’s code, which in turn prevents some
352352optimizations. However, we did get extra flexibility in the code that we wrote
353353in Listing 17-5 and were able to support in Listing 17-9, so it’s a trade-off
354354to consider.
355355
356356### Object Safety Is Required for Trait Objects
357357
358- You can only make * object safe* traits into trait objects. Some complex rules
358+ You can only make * object- safe* traits into trait objects. Some complex rules
359359govern all the properties that make a trait object safe, but in practice, only
360360two rules are relevant. A trait is object safe if all the methods defined in
361361the trait have the following properties:
@@ -391,7 +391,7 @@ of `Vec`. The signature of `clone` needs to know what type will stand in for
391391` Self ` , because that’s the return type.
392392
393393The compiler will indicate when you’re trying to do something that violates the
394- rules of object safety in regards to trait objects. For example, let’s say we
394+ rules of object safety in regard to trait objects. For example, let’s say we
395395tried to implement the ` Screen ` struct in Listing 17-4 to hold types that
396396implement the ` Clone ` trait instead of the ` Draw ` trait, like this:
397397
0 commit comments