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:
parent
a883a569d0
commit
7395725edf
@ -3,37 +3,30 @@
|
||||
Rust lets you abstract over types with traits. They're similar to interfaces:
|
||||
|
||||
```rust,editable
|
||||
struct Dog { name: String, age: i8 }
|
||||
struct Cat { lives: i8 } // No name needed, cats won't respond anyway.
|
||||
|
||||
trait Pet {
|
||||
fn name(&self) -> String;
|
||||
fn talk(&self) -> String;
|
||||
}
|
||||
|
||||
struct Dog {
|
||||
name: 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<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() {
|
||||
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);
|
||||
}
|
||||
```
|
||||
|
@ -3,76 +3,80 @@
|
||||
Trait objects allow for values of different types, for instance in a collection:
|
||||
|
||||
```rust,editable
|
||||
struct Dog { name: String, age: i8 }
|
||||
struct Cat { lives: i8 } // No name needed, cats won't respond anyway.
|
||||
|
||||
trait Pet {
|
||||
fn name(&self) -> String;
|
||||
fn talk(&self) -> String;
|
||||
}
|
||||
|
||||
struct Dog {
|
||||
name: 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<Box<dyn Pet>> = 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" | :
|
||||
: : : | | | +---------------+ :
|
||||
`- - - - - - - - - - - - - -' : | | | :
|
||||
: | | | +----------------------+ :
|
||||
: | | '---->| "<Dog as Pet>::name" | :
|
||||
: | | +----------------------+ :
|
||||
: | | :
|
||||
: | | +-+ :
|
||||
: | '-->|\| :
|
||||
: | +-+ :
|
||||
: | :
|
||||
: | +----------------------+ :
|
||||
: '---->| "<Cat as Pet>::name" | :
|
||||
: pets : : +----+----+----+----+ :
|
||||
: +-----------+-------+ : : +-----+-----+ .->| F | i | d | o | :
|
||||
: | ptr | o---+---+-----+-->| o o | o o | | +----+----+----+----+ :
|
||||
: | len | 2 | : : +-|-|-+-|-|-+ `---------. :
|
||||
: | capacity | 2 | : : | | | | data | :
|
||||
: +-----------+-------+ : : | | | | +-------+--|-------+ :
|
||||
: : : | | | '-->| name | o, 4, 4 | :
|
||||
: : : | | | | age | 5 | :
|
||||
`- - - - - - - - - - - - - -' : | | | +-------+----------+ :
|
||||
: | | | :
|
||||
: | | | vtable :
|
||||
: | | | +----------------------+ :
|
||||
: | | '---->| "<Dog as Pet>::talk" | :
|
||||
: | | +----------------------+ :
|
||||
: | | :
|
||||
: | | data :
|
||||
: | | +-------+-------+ :
|
||||
: | '-->| lives | 9 | :
|
||||
: | +-------+-------+ :
|
||||
: | :
|
||||
: | vtable :
|
||||
: | +----------------------+ :
|
||||
: '---->| "<Cat as Pet>::talk" | :
|
||||
: +----------------------+ :
|
||||
: :
|
||||
'- - - - - - - - - - - - - - - - - - - - - - -'
|
||||
|
||||
```
|
||||
|
||||
<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.
|
||||
* `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<dyn Pet>` 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::<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>>());
|
||||
```
|
||||
|
||||
[virtual method table]: https://en.wikipedia.org/wiki/Virtual_method_table
|
||||
|
||||
</details>
|
||||
|
Loading…
x
Reference in New Issue
Block a user