From daaabce1eedc61158a6fd7a0ee963d5211d222f8 Mon Sep 17 00:00:00 2001 From: Nicole LeGare Date: Thu, 30 Jan 2025 17:11:17 -0800 Subject: [PATCH] Remove `dyn Trait` slides --- src/SUMMARY.md | 2 - src/generics/dyn-trait.md | 87 ----------------------- src/smart-pointers/trait-objects.md | 104 ---------------------------- 3 files changed, 193 deletions(-) delete mode 100644 src/generics/dyn-trait.md delete mode 100644 src/smart-pointers/trait-objects.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 8a7f68ce9145..bdefbccb75e8 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -101,7 +101,6 @@ - [Generic Data Types](generics/generic-data.md) - [Generic Traits](generics/generic-traits.md) - [`impl Trait`](generics/impl-trait.md) - - [`dyn Trait`](generics/dyn-trait.md) - [Exercise: Generic `min`](generics/exercise.md) - [Solution](generics/solution.md) @@ -152,7 +151,6 @@ - [Smart Pointers](smart-pointers.md) - [`Box`](smart-pointers/box.md) - [`Rc`](smart-pointers/rc.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 deleted file mode 100644 index 129238d73460..000000000000 --- a/src/generics/dyn-trait.md +++ /dev/null @@ -1,87 +0,0 @@ ---- -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 deleted file mode 100644 index f66b27b6ef6b..000000000000 --- a/src/smart-pointers/trait-objects.md +++ /dev/null @@ -1,104 +0,0 @@ ---- -minutes: 10 ---- - -# Owned Trait Objects - -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 { - 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!") - } -} - -fn main() { - let pets: Vec> = vec![ - Box::new(Cat { lives: 9 }), - Box::new(Dog { name: String::from("Fido"), age: 5 }), - ]; - for pet in pets { - println!("Hello, who are you? {}", pet.talk()); - } -} -``` - -Memory layout after allocating `pets`: - -```bob - Stack Heap -.- - - - - - - - - - - - - - - -. .- - - - - - - - - - - - - - - - - - - - - - -. -: : : : -: "pets: Vec>" : : "data: Cat" +----+----+----+----+ : -: +-----------+-------+ : : +-------+-------+ | F | i | d | o | : -: | ptr | o---+-------+--. : | lives | 9 | +----+----+----+----+ : -: | len | 2 | : | : +-------+-------+ ^ : -: | capacity | 2 | : | : ^ | : -: +-----------+-------+ : | : | '-------. : -: : | : | data:"Dog"| : -: : | : | +-------+--|-------+ : -`- - - - - - - - - - - - - - - -' | : +---|-+-----+ | name | o, 4, 4 | : - `--+-->| o o | o o-|----->| age | 5 | : - : +-|---+-|---+ +-------+----------+ : - : | | : - `- - -| - - |- - - - - - - - - - - - - - - - -' - | | - | | "Program text" - .- - -| - - |- - - - - - - - - - - - - - - - -. - : | | vtable : - : | | +----------------------+ : - : | `----->| "::talk" | : - : | +----------------------+ : - : | vtable : - : | +----------------------+ : - : '----------->| "::talk" | : - : +----------------------+ : - : : - '- - - - - - - - - - - - - - - - - - - - - - -' -``` - -
- -- Types that implement a given trait may be of different sizes. This makes it - impossible to have things like `Vec` in the example above. -- `dyn Pet` is a way to tell the compiler about a dynamically sized type that - implements `Pet`. -- In the example, `pets` is allocated on the stack and the vector data is on the - heap. The two vector elements are _fat pointers_: - - A fat pointer is a double-width pointer. It has two components: a pointer to - the actual object and a pointer to the [virtual method table] (vtable) for - the `Pet` implementation of that particular object. - - The data for the `Dog` named Fido is the `name` and `age` fields. The `Cat` - has a `lives` field. -- Compare these outputs in the above example: - ```rust,ignore - println!("{} {}", std::mem::size_of::(), std::mem::size_of::()); - println!("{} {}", std::mem::size_of::<&Dog>(), std::mem::size_of::<&Cat>()); - println!("{}", std::mem::size_of::<&dyn Pet>()); - println!("{}", std::mem::size_of::>()); - ``` - -[virtual method table]: https://en.wikipedia.org/wiki/Virtual_method_table - -