diff --git a/src/traits.md b/src/traits.md index 1fc7231bf8a1..7757779a97c7 100644 --- a/src/traits.md +++ b/src/traits.md @@ -3,37 +3,30 @@ Rust lets you abstract over types with traits. They're similar to interfaces: ```rust,editable -trait Pet { - fn name(&self) -> String; -} +struct Dog { name: String, age: i8 } +struct Cat { lives: i8 } // No name needed, cats won't respond anyway. -struct Dog { - name: String, +trait Pet { + fn talk(&self) -> String; } -struct Cat; - impl Pet for Dog { - fn name(&self) -> String { - self.name.clone() - } + fn talk(&self) -> String { format!("Woof, my name is {}!", self.name) } } impl Pet for Cat { - fn name(&self) -> String { - String::from("The cat") // No name, cats won't respond to it anyway. - } + fn talk(&self) -> String { String::from("Miau!") } } fn greet(pet: &P) { - println!("Who's a cutie? {} is!", pet.name()); + println!("Oh you're a cutie! What's your name? {}", pet.talk()); } fn main() { - let fido = Dog { name: "Fido".into() }; - greet(&fido); + let captain_floof = Cat { lives: 9 }; + let fido = Dog { name: String::from("Fido"), age: 5 }; - let captain_floof = Cat; greet(&captain_floof); + greet(&fido); } ``` diff --git a/src/traits/trait-objects.md b/src/traits/trait-objects.md index 584c819c848c..2f57bf9fe5a4 100644 --- a/src/traits/trait-objects.md +++ b/src/traits/trait-objects.md @@ -3,76 +3,80 @@ Trait objects allow for values of different types, for instance in a collection: ```rust,editable -trait Pet { - fn name(&self) -> String; -} +struct Dog { name: String, age: i8 } +struct Cat { lives: i8 } // No name needed, cats won't respond anyway. -struct Dog { - name: String, +trait Pet { + fn talk(&self) -> String; } -struct Cat; - impl Pet for Dog { - fn name(&self) -> String { - self.name.clone() - } + fn talk(&self) -> String { format!("Woof, my name is {}!", self.name) } } impl Pet for Cat { - fn name(&self) -> String { - String::from("The cat") // No name, cats won't respond to it anyway. - } + fn talk(&self) -> String { String::from("Miau!") } } fn main() { let pets: Vec> = vec![ - Box::new(Cat), - Box::new(Dog { name: String::from("Fido") }), + Box::new(Cat { lives: 9 }), + Box::new(Dog { name: String::from("Fido"), age: 5 }), ]; for pet in pets { - println!("Hello {}!", pet.name()); + println!("Hello, who are you? {}", pet.talk()); } } ``` - Memory layout after allocating `pets`: ```bob Stack Heap .- - - - - - - - - - - - - -. .- - - - - - - - - - - - - - - - - - - - - - -. : : : : -: pets : : : -: +-----------+-------+ : : +-----+-----+ : -: | ptr | o---+---+-----+-->| o o | o o | : -: | len | 2 | : : +-|-|-+-|-|-+ : -: | capacity | 2 | : : | | | | +---------------+ : -: +-----------+-------+ : : | | | '-->| name: "Fido" | : -: : : | | | +---------------+ : -`- - - - - - - - - - - - - -' : | | | : - : | | | +----------------------+ : - : | | '---->| "::name" | : - : | | +----------------------+ : - : | | : - : | | +-+ : - : | '-->|\| : - : | +-+ : - : | : - : | +----------------------+ : - : '---->| "::name" | : +: pets : : +----+----+----+----+ : +: +-----------+-------+ : : +-----+-----+ .->| F | i | d | o | : +: | ptr | o---+---+-----+-->| o o | o o | | +----+----+----+----+ : +: | len | 2 | : : +-|-|-+-|-|-+ `---------. : +: | capacity | 2 | : : | | | | data | : +: +-----------+-------+ : : | | | | +-------+--|-------+ : +: : : | | | '-->| name | o, 4, 4 | : +: : : | | | | age | 5 | : +`- - - - - - - - - - - - - -' : | | | +-------+----------+ : + : | | | : + : | | | vtable : + : | | | +----------------------+ : + : | | '---->| "::talk" | : + : | | +----------------------+ : + : | | : + : | | data : + : | | +-------+-------+ : + : | '-->| lives | 9 | : + : | +-------+-------+ : + : | : + : | 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` holds *fat pointers* to objects that implement `Pet`. The fat pointer consists of two components, a pointer to the actual object and a pointer to the virtual method table for the `Pet` implementation of that particular object. -* Compare these outputs in the above example: +- 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>()); @@ -80,4 +84,6 @@ Memory layout after allocating `pets`: println!("{}", std::mem::size_of::>()); ``` +[virtual method table]: https://en.wikipedia.org/wiki/Virtual_method_table +