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

Expand Traits, add associated types (#1815)

This breaks the "Traits" slide into three smaller sub-slides. It also
addresses part of #1511 by explicitly addressing associated types.

---------

Co-authored-by: Martin Geisler <martin@geisler.net>
This commit is contained in:
Dustin J. Mitchell 2024-02-20 11:41:53 -05:00 committed by GitHub
parent 9d63f23f1d
commit 95fce416ce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 87 additions and 37 deletions

View File

@ -80,6 +80,8 @@
- [Methods and Traits](methods-and-traits.md)
- [Methods](methods-and-traits/methods.md)
- [Traits](methods-and-traits/traits.md)
- [Implmementing Traits](methods-and-traits/traits/implementing.md)
- [Associated Types](methods-and-traits/traits/associated-types.md)
- [Deriving](methods-and-traits/deriving.md)
- [Exercise: Generic Logger](methods-and-traits/exercise.md)
- [Solution](methods-and-traits/solution.md)

View File

@ -1,5 +1,5 @@
---
minutes: 8
minutes: 10
---
# Traits
@ -7,40 +7,12 @@ minutes: 8
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,
}
trait Pet {
/// Return a sentence from this pet.
fn talk(&self) -> String;
fn greet(&self) {
println!("Oh you're a cutie! What's your name? {}", self.talk());
}
}
impl Pet for Dog {
fn talk(&self) -> String {
format!("Woof, my name is {}!", self.name)
}
}
impl Pet for Cat {
fn talk(&self) -> String {
String::from("Miau!")
}
}
fn main() {
let captain_floof = Cat { lives: 9 };
let fido = Dog { name: String::from("Fido"), age: 5 };
captain_floof.greet();
fido.greet();
/// Print a string to the terminal greeting this pet.
fn greet(&self);
}
```
@ -49,10 +21,7 @@ fn main() {
- A trait defines a number of methods that types must have in order to implement
the trait.
- Traits are implemented in an `impl <trait> for <type> { .. }` block.
- Traits may specify pre-implemented (provided) methods and methods that users
are required to implement themselves. Provided methods can rely on required
methods. In this case, `greet` is provided, and relies on `talk`.
- In the "Generics" segment, next, we will see how to build functionality that
is generic over all types implementing a trait.
</details>

View File

@ -0,0 +1,37 @@
# Associated Types
Associated types allow are placeholder types which are filled in by the trait
implementation.
```rust,editable
#[derive(Debug)]
struct Meters(i32);
#[derive(Debug)]
struct MetersSquared(i32);
trait Multiply {
type Output;
fn multiply(&self, other: &Self) -> Self::Output;
}
impl Multiply for Meters {
type Output = MetersSquared;
fn multiply(&self, other: &Self) -> Self::Output {
MetersSquared(self.0 * other.0)
}
}
fn main() {
println!("{:?}", Meters(10).multiply(&Meters(20)));
}
```
<details>
- Associated types are sometimes also called "output types". The key observation
is that the implementer, not the caller, chooses this type.
- Many standard library traits have associated types, including arithmetic
operators and `Iterator`.
</details>

View File

@ -0,0 +1,42 @@
# Implementing Traits
```rust,editable
trait Pet {
fn talk(&self) -> String;
fn greet(&self) {
println!("Oh you're a cutie! What's your name? {}", self.talk());
}
}
struct Dog {
name: String,
age: i8,
}
impl Pet for Dog {
fn talk(&self) -> String {
format!("Woof, my name is {}!", self.name)
}
}
fn main() {
let fido = Dog { name: String::from("Fido"), age: 5 };
fido.greet();
}
```
<details>
- To implement `Trait` for `Type`, you use an `impl Trait for Type { .. }`
block.
- Unlike Go interfaces, just having matching methods is not enough: a `Cat` type
with a `talk()` method would not automatically satisfy `Pet` unless it is in
an `impl Pet` block.
- Traits may provide default implementations of some methods. Default
implementations can rely on all the methods of the trait. In this case,
`greet` is provided, and relies on `talk`.
</details>