Обобщённые типы данных
С помощью обобщений можно абстрагироваться от конкретного типа поля. Возвращаясь к упражнению из предыдущего сегмента:
// Copyright 2023 Google LLC
// SPDX-License-Identifier: Apache-2.0
pub trait Logger {
/// Записывает сообщение с заданным уровнем подробности.
fn log(&self, verbosity: u8, message: &str);
}
struct StderrLogger;
impl Logger for StderrLogger {
fn log(&self, verbosity: u8, message: &str) {
eprintln!("[stderr] verbosity={verbosity}: {message}");
}
}
struct StdoutLogger;
impl Logger for StdoutLogger {
fn log(&self, verbosity: u8, message: &str) {
println!("[stdout] verbosity={verbosity}: {message}");
}
}
/// Записывает только сообщения до заданного уровня подробности.
struct VerbosityFilter<L> {
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() {
let err = VerbosityFilter { max_verbosity: 3, inner: StderrLogger };
err.log(5, "FYI");
err.log(2, "Uhoh");
let out = VerbosityFilter { max_verbosity: 3, inner: StdoutLogger };
out.log(5, "FYI");
out.log(2, "Uhoh");
}
This slide should take about 10 minutes.
- Вопрос: Почему
Lуказан дважды вimpl<L: Logger> .. VerbosityFilter<L>? Разве это не избыточно?- Это потому, что это обобщённый блок реализации для обобщённого типа. Они независимо обобщены.
- Это означает, что эти методы определены для любого
L. - Можно написать
impl VerbosityFilter<StderrLogger> { .. }.VerbosityFilterпо-прежнему обобщён, и можно использоватьVerbosityFilter<f64>, но методы в этом блоке будут доступны только дляVerbosityFilter<StderrLogger>.
- Обратите внимание, что мы не задаём ограничение трейта для самого типа
VerbosityFilter. Туда тоже можно поставить ограничения, но в Rust обычно ограничения трейтов указывают только в блокахimpl.