Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Avoid naming struct field like trait method #1294

Merged
merged 1 commit into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 10 additions & 17 deletions src/traits.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<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);
}
```
86 changes: 46 additions & 40 deletions src/traits/trait-objects.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,81 +3,87 @@
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<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>());
println!("{}", std::mem::size_of::<&dyn Pet>());
println!("{}", std::mem::size_of::<Box<dyn Pet>>());
```

[virtual method table]: https://en.wikipedia.org/wiki/Virtual_method_table

</details>