mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-06-01 23:29:38 +02:00
Be clear that the methods-and-traits exercise does not require generics (#2568)
When teaching the course, I got a little tripped up thinking students would need to make the `VerbosityFilter` generic over `Logger`. Let's be clearer that this is not required, and will be described later. This also updates the generic-types slide to repeat the exercise, completing that thought.
This commit is contained in:
parent
15e46379b1
commit
b3c57e4cbf
@ -4,46 +4,54 @@ minutes: 10
|
|||||||
|
|
||||||
# Generic Data Types
|
# Generic Data Types
|
||||||
|
|
||||||
You can use generics to abstract over the concrete field type:
|
You can use generics to abstract over the concrete field type. Returning to the
|
||||||
|
exercise for the previous segment:
|
||||||
|
|
||||||
```rust,editable
|
```rust,editable
|
||||||
#[derive(Debug)]
|
pub trait Logger {
|
||||||
struct Point<T> {
|
/// Log a message at the given verbosity level.
|
||||||
x: T,
|
fn log(&self, verbosity: u8, message: &str);
|
||||||
y: T,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Point<T> {
|
struct StderrLogger;
|
||||||
fn coords(&self) -> (&T, &T) {
|
|
||||||
(&self.x, &self.y)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_x(&mut self, x: T) {
|
impl Logger for StderrLogger {
|
||||||
self.x = x;
|
fn log(&self, verbosity: u8, message: &str) {
|
||||||
|
eprintln!("verbosity={verbosity}: {message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Only log messages up to the given verbosity level.
|
||||||
|
struct VerbosityFilter<L: Logger> {
|
||||||
|
max_verbosity: u8,
|
||||||
|
inner: L,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L: Logger> Logger for VerbosityFilter<L> {
|
||||||
|
fn log(&self, verbosity: u8, message: &str) {
|
||||||
|
if verbosity <= self.max_verbosity {
|
||||||
|
self.inner.log(verbosity, message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let integer = Point { x: 5, y: 10 };
|
let logger = VerbosityFilter { max_verbosity: 3, inner: StderrLogger };
|
||||||
let float = Point { x: 1.0, y: 4.0 };
|
logger.log(5, "FYI");
|
||||||
println!("{integer:?} and {float:?}");
|
logger.log(2, "Uhoh");
|
||||||
println!("coords: {:?}", integer.coords());
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
|
||||||
- _Q:_ Why `T` is specified twice in `impl<T> Point<T> {}`? Isn't that
|
- _Q:_ Why `L` is specified twice in `impl<L: Logger> .. VerbosityFilter<L>`?
|
||||||
redundant?
|
Isn't that redundant?
|
||||||
- This is because it is a generic implementation section for generic type.
|
- This is because it is a generic implementation section for generic type.
|
||||||
They are independently generic.
|
They are independently generic.
|
||||||
- It means these methods are defined for any `T`.
|
- It means these methods are defined for any `L`.
|
||||||
- It is possible to write `impl Point<u32> { .. }`.
|
- It is possible to write `impl VerbosityFilter<StderrLogger> { .. }`.
|
||||||
- `Point` is still generic and you can use `Point<f64>`, but methods in this
|
- `VerbosityFilter` is still generic and you can use `VerbosityFilter<f64>`,
|
||||||
block will only be available for `Point<u32>`.
|
but methods in this block will only be available for
|
||||||
|
`Point<StderrLogger>`.
|
||||||
- Try declaring a new variable `let p = Point { x: 5, y: 10.0 };`. Update the
|
|
||||||
code to allow points that have elements of different types, by using two type
|
|
||||||
variables, e.g., `T` and `U`.
|
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
minutes: 20
|
minutes: 15
|
||||||
---
|
---
|
||||||
|
|
||||||
# Exercise: Logger Trait
|
# Exercise: Logger Trait
|
||||||
@ -14,13 +14,14 @@ verbosity. Your task is to write a `VerbosityFilter` type that will ignore
|
|||||||
messages above a maximum verbosity.
|
messages above a maximum verbosity.
|
||||||
|
|
||||||
This is a common pattern: a struct wrapping a trait implementation and
|
This is a common pattern: a struct wrapping a trait implementation and
|
||||||
implementing that same trait, adding behavior in the process. What other kinds
|
implementing that same trait, adding behavior in the process. In the "Generics"
|
||||||
of wrappers might be useful in a logging utility?
|
segment this afternoon, we will see how to make the wrapper generic over the
|
||||||
|
wrapped type.
|
||||||
|
|
||||||
```rust,compile_fail
|
```rust,compile_fail
|
||||||
{{#include exercise.rs:setup}}
|
{{#include exercise.rs:setup}}
|
||||||
|
|
||||||
// TODO: Define and implement `VerbosityFilter`.
|
// TODO: Implement the `Logger` trait for `VerbosityFilter`.
|
||||||
|
|
||||||
{{#include exercise.rs:main}}
|
{{#include exercise.rs:main}}
|
||||||
```
|
```
|
||||||
|
@ -26,13 +26,13 @@ impl Logger for StderrLogger {
|
|||||||
eprintln!("verbosity={verbosity}: {message}");
|
eprintln!("verbosity={verbosity}: {message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ANCHOR_END: setup
|
|
||||||
|
|
||||||
/// Only log messages up to the given verbosity level.
|
/// Only log messages up to the given verbosity level.
|
||||||
struct VerbosityFilter {
|
struct VerbosityFilter {
|
||||||
max_verbosity: u8,
|
max_verbosity: u8,
|
||||||
inner: StderrLogger,
|
inner: StderrLogger,
|
||||||
}
|
}
|
||||||
|
// ANCHOR_END: setup
|
||||||
|
|
||||||
impl Logger for VerbosityFilter {
|
impl Logger for VerbosityFilter {
|
||||||
fn log(&self, verbosity: u8, message: &str) {
|
fn log(&self, verbosity: u8, message: &str) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user