Skip to content
Merged
Show file tree
Hide file tree
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
3 changes: 2 additions & 1 deletion src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
- [Generic Traits](generics/generic-traits.md)
- [Trait Bounds](generics/trait-bounds.md)
- [`impl Trait`](generics/impl-trait.md)
- [`dyn Trait`](generics/dyn-trait.md)
- [Exercise: Generic `min`](generics/exercise.md)
- [Solution](generics/solution.md)
- [Standard Library Types](std-types.md)
Expand Down Expand Up @@ -141,7 +142,7 @@
- [Smart Pointers](smart-pointers.md)
- [`Box<T>`](smart-pointers/box.md)
- [`Rc`](smart-pointers/rc.md)
- [Trait Objects](smart-pointers/trait-objects.md)
- [Owned Trait Objects](smart-pointers/trait-objects.md)
- [Exercise: Binary Tree](smart-pointers/exercise.md)
- [Solution](smart-pointers/solution.md)

Expand Down
87 changes: 87 additions & 0 deletions src/generics/dyn-trait.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
---
minutes: 5
---

# `dyn Trait`

In addition to using traits for static dispatch via generics, Rust also supports
using them for type-erased, dynamic dispatch via trait objects:

```rust,editable
struct Dog {
name: String,
age: i8,
}
struct Cat {
lives: i8,
}

trait Pet {
fn talk(&self) -> String;
}

impl Pet for Dog {
fn talk(&self) -> String {
format!("Woof, my name is {}!", self.name)
}
}

impl Pet for Cat {
fn talk(&self) -> String {
String::from("Miau!")
}
}

// Uses generics and static dispatch.
fn generic(pet: &impl Pet) {
println!("Hello, who are you? {}", pet.talk());
}

// Uses type-erasure and dynamic dispatch.
fn dynamic(pet: &dyn Pet) {
println!("Hello, who are you? {}", pet.talk());
}

fn main() {
let cat = Cat { lives: 9 };
let dog = Dog { name: String::from("Fido"), age: 5 };

generic(&cat);
generic(&dog);

dynamic(&cat);
dynamic(&dog);
}
```

<details>

- Generics, including `impl Trait`, use monomorphization to create a specialized
instance of the function for each different type that the generic is
instantiated with. This means that calling a trait method from within a
generic function still uses static dispatch, as the compiler has full type
information and can resolve which type's trait implementation to use.

- When using `dyn Trait`, it instead uses dynamic dispatch through a
[virtual method table][vtable] (vtable). This means that there's a single
version of `fn dynamic` that is used regardless of what type of `Pet` is
passed in.

- When using `dyn Trait`, the trait object needs to be behind some kind of
indirection. In this case it's a reference, though smart pointer types like
`Box` can also be used (this will be demonstrated on day 3).

- At runtime, a `&dyn Pet` is represented as a "fat pointer", i.e. a pair of two
pointers: One pointer points to the concrete object that implements `Pet`, and
the other points to the vtable for the trait implementation for that type.
When calling the `talk` method on `&dyn Pet` the compiler looks up the
function pointer for `talk` in the vtable and then invokes the function,
passing the pointer to the `Dog` or `Cat` into that function. The compiler
doesn't need to know the concrete type of the `Pet` in order to do this.

- A `dyn Trait` is considered to be "type-erased", because we no longer have
compile-time knowledge of what the concrete type is.

[vtable]: https://en.wikipedia.org/wiki/Virtual_method_table

</details>
6 changes: 4 additions & 2 deletions src/smart-pointers/trait-objects.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
minutes: 10
---

# Trait Objects
# Owned Trait Objects

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

```rust,editable
struct Dog {
Expand Down