Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 15 additions & 16 deletions src/ch18-02-trait-objects.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ didn’t mean to pass and so should pass a different type, or we should implemen

### Trait Objects and Type Inference

One downside to using trait objects is how they interact with type inference.
One downside to using trait objects is how they interact with type inference.
For example, consider type inference for `Vec<T>`. When `T` is not a trait object,
Rust just needs to know the type of a single element in the vector to infer `T`. So
an empty vector causes a type inference error:
Expand All @@ -246,8 +246,8 @@ let v = vec!["Hello world"];
# }
```

Type inference is trickier for trait objects. For example, say we tried to factor
the `components` array in Listing 17-9 into a separate variable, like this:
Type inference is trickier for trait objects. For example, say we tried to factor
the `components` array in Listing 18-9 into a separate variable, like this:

```rust,ignore,does_not_compile
fn main() {
Expand All @@ -260,7 +260,7 @@ fn main() {
}
```

<span class="caption">Listing 17-11: Factoring the components array causes a type error</span>
<span class="caption">Listing 18-11: Factoring the components array causes a type error</span>

This refactor causes the program to no longer compile! The compiler rejects this program with
the following error:
Expand All @@ -280,34 +280,33 @@ error[E0308]: mismatched types
| |_____^ expected `SelectBox`, found `Button`
```

In Listing 17-09, the compiler understands that the `components` vector must have the type
`Vec<Box<dyn Draw>>` because that's specified in the `Screen` struct definition. But in Listing 17-11,
In Listing 18-09, the compiler understands that the `components` vector must have the type
`Vec<Box<dyn Draw>>` because that's specified in the `Screen` struct definition. But in Listing 18-11,
the compiler loses that information at the point where `components` is defined. To fix the issue, you
have to give a hint to the type inference algorithm. That can either be via an explicit cast on
any element of the vector, like this:

```rust,ignore
let components = vec![
Box::new(SelectBox { /* .. */ }) as Box<dyn Draw>,
Box::new(Button { /* .. */ }),
];
let components = vec![
Box::new(SelectBox { /* .. */ }) as Box<dyn Draw>,
Box::new(Button { /* .. */ }),
];
```

Or it can be via a type annotation on the let-binding, like this:

```rust,ignore
let components: Vec<Box<dyn Draw>> = vec![
Box::new(SelectBox { /* .. */ }),
Box::new(Button { /* .. */ }),
];
let components: Vec<Box<dyn Draw>> = vec![
Box::new(SelectBox { /* .. */ }),
Box::new(Button { /* .. */ }),
];
```

In general, it is good to be aware that using trait objects can cause a worse developer experience for
API clients in the case of type inference.

<!-- END INTERVENTION: cce62358-5291-4eb3-84d6-fbc570873ee3 -->


### Trait Objects Perform Dynamic Dispatch

Recall in [“Performance of Code Using
Expand All @@ -329,7 +328,7 @@ call. This lookup incurs a runtime cost that doesn’t occur with static dispatc
Dynamic dispatch also prevents the compiler from choosing to inline a method’s
code, which in turn prevents some optimizations, and Rust has some rules, called
_dyn compatibility_, about where you can and cannot use dynamic dispatch. Those
rules are beyond the scope of this discussion, but you can read more about them
rules are beyond the scope of this discussion, but you can read more about them
[in the reference][dyn-compatibility]. However, we did get extra flexibility in
the code that we wrote in Listing 18-5 and were able to support in Listing 18-9,
so it’s a trade-off to consider.
Expand Down