Skip to content

Commit 59bf3bd

Browse files
authored
Add a slide introducing dyn Trait in Generics (#2108)
I've been thinking it'd be simpler to introduce `dyn Trait` via `&dyn Trait` rather than waiting for the smart pointers section and `Box<dyn Trait>`. This PR adds a slide to the Generics section that introduces `&dyn Trait` and compares it to `&impl Trait`, juxtaposing monomorphization and static dispatch against type-erasure and dynamic dispatch. I've then updated the existing trait object slide to call back to the earlier introduction, and call out that using `Box<dyn Trait>` gives you an owned trait object rather than a borrowed one.
1 parent 06264e8 commit 59bf3bd

File tree

3 files changed

+93
-3
lines changed

3 files changed

+93
-3
lines changed

src/SUMMARY.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@
100100
- [Generic Traits](generics/generic-traits.md)
101101
- [Trait Bounds](generics/trait-bounds.md)
102102
- [`impl Trait`](generics/impl-trait.md)
103+
- [`dyn Trait`](generics/dyn-trait.md)
103104
- [Exercise: Generic `min`](generics/exercise.md)
104105
- [Solution](generics/solution.md)
105106
- [Standard Library Types](std-types.md)
@@ -141,7 +142,7 @@
141142
- [Smart Pointers](smart-pointers.md)
142143
- [`Box<T>`](smart-pointers/box.md)
143144
- [`Rc`](smart-pointers/rc.md)
144-
- [Trait Objects](smart-pointers/trait-objects.md)
145+
- [Owned Trait Objects](smart-pointers/trait-objects.md)
145146
- [Exercise: Binary Tree](smart-pointers/exercise.md)
146147
- [Solution](smart-pointers/solution.md)
147148

src/generics/dyn-trait.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
---
2+
minutes: 5
3+
---
4+
5+
# `dyn Trait`
6+
7+
In addition to using traits for static dispatch via generics, Rust also supports
8+
using them for type-erased, dynamic dispatch via trait objects:
9+
10+
```rust,editable
11+
struct Dog {
12+
name: String,
13+
age: i8,
14+
}
15+
struct Cat {
16+
lives: i8,
17+
}
18+
19+
trait Pet {
20+
fn talk(&self) -> String;
21+
}
22+
23+
impl Pet for Dog {
24+
fn talk(&self) -> String {
25+
format!("Woof, my name is {}!", self.name)
26+
}
27+
}
28+
29+
impl Pet for Cat {
30+
fn talk(&self) -> String {
31+
String::from("Miau!")
32+
}
33+
}
34+
35+
// Uses generics and static dispatch.
36+
fn generic(pet: &impl Pet) {
37+
println!("Hello, who are you? {}", pet.talk());
38+
}
39+
40+
// Uses type-erasure and dynamic dispatch.
41+
fn dynamic(pet: &dyn Pet) {
42+
println!("Hello, who are you? {}", pet.talk());
43+
}
44+
45+
fn main() {
46+
let cat = Cat { lives: 9 };
47+
let dog = Dog { name: String::from("Fido"), age: 5 };
48+
49+
generic(&cat);
50+
generic(&dog);
51+
52+
dynamic(&cat);
53+
dynamic(&dog);
54+
}
55+
```
56+
57+
<details>
58+
59+
- Generics, including `impl Trait`, use monomorphization to create a specialized
60+
instance of the function for each different type that the generic is
61+
instantiated with. This means that calling a trait method from within a
62+
generic function still uses static dispatch, as the compiler has full type
63+
information and can resolve which type's trait implementation to use.
64+
65+
- When using `dyn Trait`, it instead uses dynamic dispatch through a
66+
[virtual method table][vtable] (vtable). This means that there's a single
67+
version of `fn dynamic` that is used regardless of what type of `Pet` is
68+
passed in.
69+
70+
- When using `dyn Trait`, the trait object needs to be behind some kind of
71+
indirection. In this case it's a reference, though smart pointer types like
72+
`Box` can also be used (this will be demonstrated on day 3).
73+
74+
- At runtime, a `&dyn Pet` is represented as a "fat pointer", i.e. a pair of two
75+
pointers: One pointer points to the concrete object that implements `Pet`, and
76+
the other points to the vtable for the trait implementation for that type.
77+
When calling the `talk` method on `&dyn Pet` the compiler looks up the
78+
function pointer for `talk` in the vtable and then invokes the function,
79+
passing the pointer to the `Dog` or `Cat` into that function. The compiler
80+
doesn't need to know the concrete type of the `Pet` in order to do this.
81+
82+
- A `dyn Trait` is considered to be "type-erased", because we no longer have
83+
compile-time knowledge of what the concrete type is.
84+
85+
[vtable]: https://en.wikipedia.org/wiki/Virtual_method_table
86+
87+
</details>

src/smart-pointers/trait-objects.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
minutes: 10
33
---
44

5-
# Trait Objects
5+
# Owned Trait Objects
66

7-
Trait objects allow for values of different types, for instance in a collection:
7+
We previously saw how trait objects can be used with references, e.g `&dyn Pet`.
8+
However, we can also use trait objects with smart pointers like `Box` to create
9+
an owned trait object: `Box<dyn Pet>`.
810

911
```rust,editable
1012
struct Dog {

0 commit comments

Comments
 (0)