1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-03-06 08:49:17 +02:00

Avoid naming struct field like trait method (#1294)

The `name` struct field was confusing because it was named the same as
the trait method. The struct fields are now disjoint from the method
names — the fact that there are two separate name spaces is not the
point of these slides.

I also dropped the zero-sized type, this is also not the main focus
here.

I also compressed the code a bit to make the unimportant structs take
up less space.

Fixes #1292.
This commit is contained in:
Martin Geisler 2023-10-04 11:52:15 +02:00 committed by GitHub
parent a883a569d0
commit 7395725edf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 56 additions and 57 deletions

View File

@ -3,37 +3,30 @@
Rust lets you abstract over types with traits. They're similar to interfaces: Rust lets you abstract over types with traits. They're similar to interfaces:
```rust,editable ```rust,editable
struct Dog { name: String, age: i8 }
struct Cat { lives: i8 } // No name needed, cats won't respond anyway.
trait Pet { trait Pet {
fn name(&self) -> String; fn talk(&self) -> String;
} }
struct Dog {
name: String,
}
struct Cat;
impl Pet for Dog { impl Pet for Dog {
fn name(&self) -> String { fn talk(&self) -> String { format!("Woof, my name is {}!", self.name) }
self.name.clone()
}
} }
impl Pet for Cat { impl Pet for Cat {
fn name(&self) -> String { fn talk(&self) -> String { String::from("Miau!") }
String::from("The cat") // No name, cats won't respond to it anyway.
}
} }
fn greet<P: Pet>(pet: &P) { fn greet<P: Pet>(pet: &P) {
println!("Who's a cutie? {} is!", pet.name()); println!("Oh you're a cutie! What's your name? {}", pet.talk());
} }
fn main() { fn main() {
let fido = Dog { name: "Fido".into() }; let captain_floof = Cat { lives: 9 };
greet(&fido); let fido = Dog { name: String::from("Fido"), age: 5 };
let captain_floof = Cat;
greet(&captain_floof); greet(&captain_floof);
greet(&fido);
} }
``` ```

View File

@ -3,76 +3,80 @@
Trait objects allow for values of different types, for instance in a collection: Trait objects allow for values of different types, for instance in a collection:
```rust,editable ```rust,editable
struct Dog { name: String, age: i8 }
struct Cat { lives: i8 } // No name needed, cats won't respond anyway.
trait Pet { trait Pet {
fn name(&self) -> String; fn talk(&self) -> String;
} }
struct Dog {
name: String,
}
struct Cat;
impl Pet for Dog { impl Pet for Dog {
fn name(&self) -> String { fn talk(&self) -> String { format!("Woof, my name is {}!", self.name) }
self.name.clone()
}
} }
impl Pet for Cat { impl Pet for Cat {
fn name(&self) -> String { fn talk(&self) -> String { String::from("Miau!") }
String::from("The cat") // No name, cats won't respond to it anyway.
}
} }
fn main() { fn main() {
let pets: Vec<Box<dyn Pet>> = vec![ let pets: Vec<Box<dyn Pet>> = vec![
Box::new(Cat), Box::new(Cat { lives: 9 }),
Box::new(Dog { name: String::from("Fido") }), Box::new(Dog { name: String::from("Fido"), age: 5 }),
]; ];
for pet in pets { for pet in pets {
println!("Hello {}!", pet.name()); println!("Hello, who are you? {}", pet.talk());
} }
} }
``` ```
Memory layout after allocating `pets`: Memory layout after allocating `pets`:
```bob ```bob
Stack Heap Stack Heap
.- - - - - - - - - - - - - -. .- - - - - - - - - - - - - - - - - - - - - - -. .- - - - - - - - - - - - - -. .- - - - - - - - - - - - - - - - - - - - - - -.
: : : : : : : :
: pets : : : : pets : : +----+----+----+----+ :
: +-----------+-------+ : : +-----+-----+ : : +-----------+-------+ : : +-----+-----+ .->| F | i | d | o | :
: | ptr | o---+---+-----+-->| o o | o o | : : | ptr | o---+---+-----+-->| o o | o o | | +----+----+----+----+ :
: | len | 2 | : : +-|-|-+-|-|-+ : : | len | 2 | : : +-|-|-+-|-|-+ `---------. :
: | capacity | 2 | : : | | | | +---------------+ : : | capacity | 2 | : : | | | | data | :
: +-----------+-------+ : : | | | '-->| name: "Fido" | : : +-----------+-------+ : : | | | | +-------+--|-------+ :
: : : | | | +---------------+ : : : : | | | '-->| name | o, 4, 4 | :
`- - - - - - - - - - - - - -' : | | | : : : : | | | | age | 5 | :
: | | | +----------------------+ : `- - - - - - - - - - - - - -' : | | | +-------+----------+ :
: | | '---->| "<Dog as Pet>::name" | : : | | | :
: | | +----------------------+ : : | | | vtable :
: | | : : | | | +----------------------+ :
: | | +-+ : : | | '---->| "<Dog as Pet>::talk" | :
: | '-->|\| : : | | +----------------------+ :
: | +-+ : : | | :
: | : : | | data :
: | +----------------------+ : : | | +-------+-------+ :
: '---->| "<Cat as Pet>::name" | : : | '-->| lives | 9 | :
: | +-------+-------+ :
: | :
: | vtable :
: | +----------------------+ :
: '---->| "<Cat as Pet>::talk" | :
: +----------------------+ : : +----------------------+ :
: : : :
'- - - - - - - - - - - - - - - - - - - - - - -' '- - - - - - - - - - - - - - - - - - - - - - -'
``` ```
<details> <details>
* Types that implement a given trait may be of different sizes. This makes it impossible to have things like `Vec<Pet>` in the example above. - Types that implement a given trait may be of different sizes. This makes it
* `dyn Pet` is a way to tell the compiler about a dynamically sized type that implements `Pet`. impossible to have things like `Vec<dyn Pet>` in the example above.
* 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. - `dyn Pet` is a way to tell the compiler about a dynamically sized type that
* Compare these outputs in the above example: 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 ```rust,ignore
println!("{} {}", std::mem::size_of::<Dog>(), std::mem::size_of::<Cat>()); println!("{} {}", std::mem::size_of::<Dog>(), std::mem::size_of::<Cat>());
println!("{} {}", std::mem::size_of::<&Dog>(), std::mem::size_of::<&Cat>()); 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::<Box<dyn Pet>>()); println!("{}", std::mem::size_of::<Box<dyn Pet>>());
``` ```
[virtual method table]: https://en.wikipedia.org/wiki/Virtual_method_table
</details> </details>