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:
parent
9d63f23f1d
commit
95fce416ce
@ -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)
|
||||
|
@ -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>
|
||||
|
37
src/methods-and-traits/traits/associated-types.md
Normal file
37
src/methods-and-traits/traits/associated-types.md
Normal 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>
|
42
src/methods-and-traits/traits/implementing.md
Normal file
42
src/methods-and-traits/traits/implementing.md
Normal 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>
|
Loading…
x
Reference in New Issue
Block a user