1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-01-01 22:19:21 +02:00

Restructure Day-3 morning (#503)

* Restructure Day-3 morning
This commit is contained in:
rbehjati 2023-03-30 13:25:34 +01:00 committed by GitHub
parent 780368b4f7
commit 739b3a01e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 123 additions and 126 deletions

View File

@ -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"

View File

@ -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)

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View 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>