From 253f44a0f3510efaa533267c8246e23a88e14399 Mon Sep 17 00:00:00 2001 From: Nicole LeGare Date: Wed, 29 May 2024 17:33:37 -0700 Subject: [PATCH] Add a slide introducing `dyn Trait` in Generics --- src/SUMMARY.md | 3 +- src/generics/dyn-trait.md | 87 +++++++++++++++++++++++++++++ src/smart-pointers/trait-objects.md | 6 +- 3 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 src/generics/dyn-trait.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index ac3a343bcdda..f563654c6295 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -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`](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) diff --git a/src/generics/dyn-trait.md b/src/generics/dyn-trait.md new file mode 100644 index 000000000000..129238d73460 --- /dev/null +++ b/src/generics/dyn-trait.md @@ -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); +} +``` + +
+ +- 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 + +
diff --git a/src/smart-pointers/trait-objects.md b/src/smart-pointers/trait-objects.md index 39155765ccc6..7f0e3f51df0a 100644 --- a/src/smart-pointers/trait-objects.md +++ b/src/smart-pointers/trait-objects.md @@ -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`. ```rust,editable struct Dog {