1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2024-12-01 02:42:20 +02:00

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.
This commit is contained in:
Nicole L 2024-05-31 06:59:36 -07:00 committed by GitHub
parent 06264e8cc7
commit 59bf3bdcfb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 93 additions and 3 deletions

View File

@ -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)
@ -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)

87
src/generics/dyn-trait.md Normal file
View File

@ -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>

View File

@ -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 {