mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-01-01 22:19:21 +02:00
parent
780368b4f7
commit
739b3a01e0
@ -45,3 +45,6 @@ editable = true
|
||||
"structure.html" = "running-the-course/course-structure.html"
|
||||
"exercises/day-4/afternoon.html" = "exercises/day-4/android.html"
|
||||
"unsafe/unsafe-functions.html" = "calling-unsafe-functions.html"
|
||||
"generics/closures.html" = "traits/closures.html"
|
||||
"generics/impl-trait.html" = "traits/impl-trait.html"
|
||||
"generics/trait-bounds.html" = "traits/trait-bounds.html"
|
||||
|
@ -127,25 +127,25 @@
|
||||
----
|
||||
|
||||
- [Welcome](welcome-day-3.md)
|
||||
- [Traits](traits.md)
|
||||
- [Deriving Traits](traits/deriving-traits.md)
|
||||
- [Default Methods](traits/default-methods.md)
|
||||
- [Important Traits](traits/important-traits.md)
|
||||
- [Iterator](traits/iterator.md)
|
||||
- [FromIterator](traits/from-iterator.md)
|
||||
- [From and Into](traits/from-into.md)
|
||||
- [Read and Write](traits/read-write.md)
|
||||
- [Add, Mul, ...](traits/operators.md)
|
||||
- [Drop](traits/drop.md)
|
||||
- [Default](traits/default.md)
|
||||
- [Generics](generics.md)
|
||||
- [Generic Data Types](generics/data-types.md)
|
||||
- [Generic Methods](generics/methods.md)
|
||||
- [Trait Bounds](generics/trait-bounds.md)
|
||||
- [impl Trait](generics/impl-trait.md)
|
||||
- [Closures](generics/closures.md)
|
||||
- [Monomorphization](generics/monomorphization.md)
|
||||
- [Trait Objects](generics/trait-objects.md)
|
||||
- [Traits](traits.md)
|
||||
- [Trait Objects](traits/trait-objects.md)
|
||||
- [Deriving Traits](traits/deriving-traits.md)
|
||||
- [Default Methods](traits/default-methods.md)
|
||||
- [Trait Bounds](traits/trait-bounds.md)
|
||||
- [impl Trait](traits/impl-trait.md)
|
||||
- [Important Traits](traits/important-traits.md)
|
||||
- [Iterator](traits/iterator.md)
|
||||
- [FromIterator](traits/from-iterator.md)
|
||||
- [From and Into](traits/from-into.md)
|
||||
- [Read and Write](traits/read-write.md)
|
||||
- [Drop](traits/drop.md)
|
||||
- [Default](traits/default.md)
|
||||
- [Operators: Add, Mul, ...](traits/operators.md)
|
||||
- [Closures](traits/closures.md)
|
||||
- [Exercises](exercises/day-3/morning.md)
|
||||
- [A Simple GUI Library](exercises/day-3/simple-gui.md)
|
||||
|
||||
|
@ -15,3 +15,11 @@ fn main() {
|
||||
println!("{integer:?} and {float:?}");
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
|
||||
* Try declaring a new variable `let p = Point { x: 5, y: 10.0 };`.
|
||||
|
||||
* Fix the code to allow points that have elements of different types.
|
||||
|
||||
</details>
|
@ -1,102 +0,0 @@
|
||||
# Trait Objects
|
||||
|
||||
We've seen how a function can take arguments which implement a trait:
|
||||
|
||||
```rust,editable
|
||||
use std::fmt::Display;
|
||||
|
||||
fn print<T: Display>(x: T) {
|
||||
println!("Your value: {x}");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
print(123);
|
||||
print("Hello");
|
||||
}
|
||||
```
|
||||
|
||||
However, how can we store a collection of mixed types which implement `Display`?
|
||||
|
||||
```rust,editable,compile_fail
|
||||
fn main() {
|
||||
let displayables = vec![123, "Hello"];
|
||||
}
|
||||
```
|
||||
|
||||
For this, we need _trait objects_:
|
||||
|
||||
```rust,editable
|
||||
use std::fmt::Display;
|
||||
|
||||
fn main() {
|
||||
let displayables: Vec<Box<dyn Display>> = vec![Box::new(123), Box::new("Hello")];
|
||||
for x in displayables {
|
||||
println!("x: {x}");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Memory layout after allocating `displayables`:
|
||||
|
||||
```bob
|
||||
Stack Heap
|
||||
.- - - - - - - - - - - - - -. .- - - - - - - - - - - - - - - - - - - - - - - -.
|
||||
: : : :
|
||||
: displayables : : :
|
||||
: +-----------+-------+ : : +-----+-----+ :
|
||||
: | ptr | o---+---+-----+-->| o o | o o | :
|
||||
: | len | 2 | : : +-|-|-+-|-|-+ :
|
||||
: | capacity | 2 | : : | | | | +----+----+----+----+----+ :
|
||||
: +-----------+-------+ : : | | | '-->| H | e | l | l | o | :
|
||||
: : : | | | +----+----+----+----+----+ :
|
||||
`- - - - - - - - - - - - - -' : | | | :
|
||||
: | | | +-------------------------+ :
|
||||
: | | '---->| "<str as Display>::fmt" | :
|
||||
: | | +-------------------------+ :
|
||||
: | | :
|
||||
: | | +----+----+----+----+ :
|
||||
: | '-->| 7b | 00 | 00 | 00 | :
|
||||
: | +----+----+----+----+ :
|
||||
: | :
|
||||
: | +-------------------------+ :
|
||||
: '---->| "<i32 as Display>::fmt" | :
|
||||
: +-------------------------+ :
|
||||
: :
|
||||
: :
|
||||
'- - - - - - - - - - - - - - - - - - - - - - - -'
|
||||
```
|
||||
|
||||
Similarly, you need a trait object if you want to return different types
|
||||
implementing a trait:
|
||||
|
||||
```rust,editable
|
||||
fn numbers(n: i32) -> Box<dyn Iterator<Item=i32>> {
|
||||
if n > 0 {
|
||||
Box::new(0..n)
|
||||
} else {
|
||||
Box::new((n..0).rev())
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("{:?}", numbers(-5).collect::<Vec<_>>());
|
||||
println!("{:?}", numbers(5).collect::<Vec<_>>());
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
<details>
|
||||
|
||||
* Types that implement a given trait may be of different sizes. This makes it impossible to have things like `Vec<Display>` in the example above.
|
||||
* `dyn Display` is a way to tell the compiler about a dynamically sized type that implements `Display`.
|
||||
* In the example, `displayables` holds *fat pointers* to objects that implement `Display`. The fat pointer consists of two components, a pointer to the actual object and a pointer to the virtual method table for the `Display` implementation of that particular object.
|
||||
* Compare these outputs in the above example:
|
||||
```rust,ignore
|
||||
use std::fmt::Display;
|
||||
println!("{}", std::mem::size_of::<u32>());
|
||||
println!("{}", std::mem::size_of::<&u32>());
|
||||
println!("{}", std::mem::size_of::<&dyn Display>());
|
||||
println!("{}", std::mem::size_of::<Box<dyn Display>>());
|
||||
```
|
||||
|
||||
</details>
|
@ -25,7 +25,7 @@ impl Pet for Cat {
|
||||
}
|
||||
}
|
||||
|
||||
fn greet(pet: &impl Pet) {
|
||||
fn greet<P: Pet>(pet: &P) {
|
||||
println!("Who's a cutie? {} is!", pet.name());
|
||||
}
|
||||
|
||||
@ -37,11 +37,3 @@ fn main() {
|
||||
greet(&captain_floof);
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
|
||||
* Later sections will get into more detail on generic functions like `greet`.
|
||||
For now, students only need to know that `greet` will operate on a reference
|
||||
to anything that implements `Pet`.
|
||||
|
||||
</details>
|
||||
|
@ -26,3 +26,16 @@ fn main() {
|
||||
println!("{a:?} not_equals {b:?}: {}", a.not_equal(&b));
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
|
||||
* Traits may specify pre-implemented (default) methods and methods that users are required to
|
||||
implement themselves. Methods with default implementations can rely on required methods.
|
||||
|
||||
* Move method `not_equal` to a new trait `NotEqual`.
|
||||
|
||||
* Make `NotEqual` a super trait for `Equal`.
|
||||
|
||||
* Provide a blanket implementation of `NotEqual` for `Equal`.
|
||||
* With the blanket implementation, you no longer need `NotEqual` as a super trait for `Equal`.
|
||||
</details>
|
83
src/traits/trait-objects.md
Normal file
83
src/traits/trait-objects.md
Normal file
@ -0,0 +1,83 @@
|
||||
# Trait Objects
|
||||
|
||||
Trait objects allow for values of different types, for instance in a collection:
|
||||
|
||||
```rust
|
||||
trait Pet {
|
||||
fn name(&self) -> String;
|
||||
}
|
||||
|
||||
struct Dog {
|
||||
name: String,
|
||||
}
|
||||
|
||||
struct Cat;
|
||||
|
||||
impl Pet for Dog {
|
||||
fn name(&self) -> String {
|
||||
self.name.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Pet for Cat {
|
||||
fn name(&self) -> String {
|
||||
String::from("The cat") // No name, cats won't respond to it anyway.
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let pets: Vec<Box<dyn Pet>> = vec![
|
||||
Box::new(Dog { name: String::from("Fido") }),
|
||||
Box::new(Cat),
|
||||
];
|
||||
for pet in pets {
|
||||
println!("Hello {}!", pet.name());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
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" | :
|
||||
: +----------------------+ :
|
||||
: :
|
||||
'- - - - - - - - - - - - - - - - - - - - - - -'
|
||||
|
||||
```
|
||||
|
||||
<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:
|
||||
```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>>());
|
||||
```
|
||||
|
||||
</details>
|
Loading…
Reference in New Issue
Block a user