mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-05-19 17:03:13 +02:00
Minor fixes for Day 3 Morning (#532)
* don't explain default trait methods early * talk about Iterator before IntoIterator * Defer discussion of trait objects to that chapter * be more specific about turbofish, in speaker notes
This commit is contained in:
parent
8c56c949ef
commit
780368b4f7
@ -16,7 +16,6 @@ fn main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
* `impl Trait` cannot be used with the `::<>` turbo fish syntax.
|
|
||||||
* `impl Trait` allows you to work with types which you cannot name.
|
* `impl Trait` allows you to work with types which you cannot name.
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
@ -24,15 +23,22 @@ fn main() {
|
|||||||
The meaning of `impl Trait` is a bit different in the different positions.
|
The meaning of `impl Trait` is a bit different in the different positions.
|
||||||
|
|
||||||
* For a parameter, `impl Trait` is like an anonymous generic parameter with a trait bound.
|
* For a parameter, `impl Trait` is like an anonymous generic parameter with a trait bound.
|
||||||
|
|
||||||
* For a return type, it means that the return type is some concrete type that implements the trait,
|
* For a return type, it means that the return type is some concrete type that implements the trait,
|
||||||
without naming the type. This can be useful when you don't want to expose the concrete type in a
|
without naming the type. This can be useful when you don't want to expose the concrete type in a
|
||||||
public API.
|
public API.
|
||||||
|
|
||||||
|
Inference is hard in return position. A function returning `impl Foo` picks
|
||||||
|
the concrete type it returns, without writing it out in the source. A function
|
||||||
|
returning a generic type like `collect<B>() -> B` can return any type
|
||||||
|
satisfying `B`, and the caller may need to choose one, such as with `let x:
|
||||||
|
Vec<_> = foo.collect()` or with the turbofish, `foo.collect::<Vec<_>>()`.
|
||||||
|
|
||||||
This example is great, because it uses `impl Display` twice. It helps to explain that
|
This example is great, because it uses `impl Display` twice. It helps to explain that
|
||||||
nothing here enforces that it is _the same_ `impl Display` type. If we used a single
|
nothing here enforces that it is _the same_ `impl Display` type. If we used a single
|
||||||
`T: Display`, it would enforce the constraint that input `T` and return `T` type are the same type.
|
`T: Display`, it would enforce the constraint that input `T` and return `T` type are the same type.
|
||||||
It would not work for this particular function, as the type we expect as input is likely not
|
It would not work for this particular function, as the type we expect as input is likely not
|
||||||
what `format!` returns. If we wanted to do the same via `: Display` syntax, we'd need two
|
what `format!` returns. If we wanted to do the same via `: Display` syntax, we'd need two
|
||||||
independent generic parameters.
|
independent generic parameters.
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
@ -19,7 +19,7 @@ However, how can we store a collection of mixed types which implement `Display`?
|
|||||||
|
|
||||||
```rust,editable,compile_fail
|
```rust,editable,compile_fail
|
||||||
fn main() {
|
fn main() {
|
||||||
let xs = vec![123, "Hello"];
|
let displayables = vec![123, "Hello"];
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -29,20 +29,20 @@ For this, we need _trait objects_:
|
|||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let xs: Vec<Box<dyn Display>> = vec![Box::new(123), Box::new("Hello")];
|
let displayables: Vec<Box<dyn Display>> = vec![Box::new(123), Box::new("Hello")];
|
||||||
for x in xs {
|
for x in displayables {
|
||||||
println!("x: {x}");
|
println!("x: {x}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Memory layout after allocating `xs`:
|
Memory layout after allocating `displayables`:
|
||||||
|
|
||||||
```bob
|
```bob
|
||||||
Stack Heap
|
Stack Heap
|
||||||
.- - - - - - - - - - - - - -. .- - - - - - - - - - - - - - - - - - - - - - - -.
|
.- - - - - - - - - - - - - -. .- - - - - - - - - - - - - - - - - - - - - - - -.
|
||||||
: : : :
|
: : : :
|
||||||
: xs : : :
|
: displayables : : :
|
||||||
: +-----------+-------+ : : +-----+-----+ :
|
: +-----------+-------+ : : +-----+-----+ :
|
||||||
: | ptr | o---+---+-----+-->| o o | o o | :
|
: | ptr | o---+---+-----+-->| o o | o o | :
|
||||||
: | len | 2 | : : +-|-|-+-|-|-+ :
|
: | len | 2 | : : +-|-|-+-|-|-+ :
|
||||||
@ -84,3 +84,19 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<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>
|
||||||
|
@ -3,51 +3,45 @@
|
|||||||
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
|
||||||
trait Greet {
|
trait Pet {
|
||||||
fn say_hello(&self);
|
fn name(&self) -> String;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Dog {
|
struct Dog {
|
||||||
name: String,
|
name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Cat; // No name, cats won't respond to it anyway.
|
struct Cat;
|
||||||
|
|
||||||
impl Greet for Dog {
|
impl Pet for Dog {
|
||||||
fn say_hello(&self) {
|
fn name(&self) -> String {
|
||||||
println!("Wuf, my name is {}!", self.name);
|
self.name.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Greet for Cat {
|
impl Pet for Cat {
|
||||||
fn say_hello(&self) {
|
fn name(&self) -> String {
|
||||||
println!("Miau!");
|
String::from("The cat") // No name, cats won't respond to it anyway.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn greet(pet: &impl Pet) {
|
||||||
|
println!("Who's a cutie? {} is!", pet.name());
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let pets: Vec<Box<dyn Greet>> = vec![
|
let fido = Dog { name: "Fido".into() };
|
||||||
Box::new(Dog { name: String::from("Fido") }),
|
greet(&fido);
|
||||||
Box::new(Cat),
|
|
||||||
];
|
let captain_floof = Cat;
|
||||||
for pet in pets {
|
greet(&captain_floof);
|
||||||
pet.say_hello();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<details>
|
<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.
|
* Later sections will get into more detail on generic functions like `greet`.
|
||||||
* Types that implement a given trait may be of different sizes. This makes it impossible to have things like `Vec<Greet>` in the example above.
|
For now, students only need to know that `greet` will operate on a reference
|
||||||
* `dyn Greet` is a way to tell the compiler about a dynamically sized type that implements `Greet`.
|
to anything that implements `Pet`.
|
||||||
* In the example, `pets` holds Fat Pointers to objects that implement `Greet`. The Fat Pointer consists of two components, a pointer to the actual object and a pointer to the virtual method table for the `Greet` 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 Greet>());
|
|
||||||
println!("{}", std::mem::size_of::<Box<dyn Greet>>());
|
|
||||||
```
|
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
@ -29,13 +29,16 @@ fn main() {
|
|||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
|
||||||
* `IntoIterator` is the trait that makes for loops work. It is implemented by collection types such as
|
|
||||||
`Vec<T>` and references to them such as `&Vec<T>` and `&[T]`. Ranges also implement it.
|
|
||||||
* The `Iterator` trait implements many common functional programming operations over collections
|
* The `Iterator` trait implements many common functional programming operations over collections
|
||||||
(e.g. `map`, `filter`, `reduce`, etc). This is the trait where you can find all the documentation
|
(e.g. `map`, `filter`, `reduce`, etc). This is the trait where you can find all the documentation
|
||||||
about them. In Rust these functions should produce the code as efficient as equivalent imperative
|
about them. In Rust these functions should produce the code as efficient as equivalent imperative
|
||||||
implementations.
|
implementations.
|
||||||
|
|
||||||
|
* `IntoIterator` is the trait that makes for loops work. It is implemented by collection types such as
|
||||||
|
`Vec<T>` and references to them such as `&Vec<T>` and `&[T]`. Ranges also implement it. This is why
|
||||||
|
you can iterate over a vector with `for i in some_vec { .. }` but
|
||||||
|
`some_vec.next()` doesn't exist.
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
[1]: https://doc.rust-lang.org/std/iter/trait.Iterator.html
|
[1]: https://doc.rust-lang.org/std/iter/trait.Iterator.html
|
||||||
|
Loading…
x
Reference in New Issue
Block a user