1
0
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:
Dustin J. Mitchell 2025-01-23 03:40:59 -05:00 committed by GitHub
parent 15e46379b1
commit b3c57e4cbf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 39 additions and 30 deletions

View File

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

View File

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

View File

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