Обобщённые реализации трейтов (blanket)
Если трейт локальный, мы можем реализовать его для любого количества типов. Как далеко можно зайти?
// Copyright 2025 Google LLC
// SPDX-License-Identifier: Apache-2.0
pub trait PrettyPrint {
fn pretty_print(&self);
}
// Обобщённая реализация! Если что-то реализует Display, то оно реализует
// PrettyPrint.
impl<T> PrettyPrint for T
where
T: std::fmt::Display,
{
fn pretty_print(&self) {
println!("{self}")
}
}
-
Субъектом реализации трейта в месте его определения может быть что угодно, включая
Tбез ограничений.С
T, о котором мы ничего не знаем, ничего нельзя сделать, поэтому такое встречается редко. -
Условные обобщённые реализации гораздо полезнее, и вы с большей вероятностью будете встречать и писать их сами.
Такие реализации содержат ограничение на трейт, например
impl <T: Display> ToString for T {...}В примере выше у нас есть обобщённая реализация для всех типов, реализующих Display. Реализация получает из ограничений трейта одну информацию: тип реализует
Display::fmt.Этого достаточно, чтобы написать реализацию красивого вывода в консоль.
-
Будьте осторожны с такими реализациями: они могут помешать пользователям ниже по цепочке зависимостей реализовать более подходящую.
Выше пример не написан для
Debug, потому что тогда почти все типы реализовали быPrettyPrint, аDebugсемантически не похож наDisplay: он предназначен для отладочного вывода, а не для того, что удобно читать человеку.
Справка:
- https://doc.rust-lang.org/reference/glossary.html#blanket-implementation