Расширение других трейтов
Как и в случае с типами, иногда желательно расширить внешние трейты. В частности, чтобы добавить новые методы всем типам, реализующим заданный трейт.
// Copyright 2025 Google LLC
// SPDX-License-Identifier: Apache-2.0
mod ext {
use std::fmt::Display;
pub trait DisplayExt {
fn quoted(&self) -> String;
}
impl<T: Display> DisplayExt for T {
fn quoted(&self) -> String {
format!("'{}'", self)
}
}
}
fn main() {
use ext::DisplayExt as _;
assert_eq!("dad".quoted(), "'dad'");
assert_eq!(4.quoted(), "'4'");
assert_eq!(true.quoted(), "'true'");
}
-
Обратите внимание, как мы добавили новое поведение сразу нескольким типам.
.quoted()можно вызывать для строковых срезов, чисел и булевых значений, так как все они реализуют трейтDisplay.Эта разновидность паттерна трейта-расширения использует обобщённые реализации (blanket).
Обобщённая реализация (blanket impl) реализует трейт для всех типов
T, которые удовлетворяют ограничениям трейтов, указанным в блокеimpl. В данном случае единственное требование — чтобыTреализовывал трейтDisplay. -
Обратите внимание слушателей на реализацию
DisplayExt::quoted: мы не можем делать никаких предположений оT, кроме того, что он реализуетDisplay. Вся наша логика должна либо использовать методы изDisplay, либо функции и макросы, которые не требуют других трейтов.Например, мы можем вызвать
format!сT, но не можем вызвать.to_uppercase(), потому чтоTне обязательно являетсяString.Мы могли бы ввести дополнительные ограничения трейтов для
T, но это сузило бы круг типов, которые могут использовать трейт-расширение. -
По соглашению трейт-расширение называют по имени расширяемого трейта с суффиксом
Ext. В примере выше этоDisplayExt. -
Обратите внимание, что вручную реализовать
DisplayExtдля типов, которые не реализуютDisplay, всё равно можно. Если вы хотите запретить прямые реализации трейта, можно сделатьDisplayсупертрейтом дляDisplayExt. Тогда типы, не реализующиеDisplay, не смогут реализовать этот трейт. -
Существуют целые крейты, которые расширяют трейты стандартной библиотеки новой функциональностью.
-
Крейт
itertoolsпредоставляет трейтItertools, который расширяетIterator. Он добавляет множество адаптеров итераторов, напримерinterleaveиunique. Он предоставляет новые алгоритмические строительные блоки для конвейеров итераторов, построенных с помощью цепочек вызовов методов. -
Крейт
futuresпредоставляет трейтFutureExt, который расширяет трейтFutureновыми комбинаторами и вспомогательными методами.
-
Для дальнейшего изучения
-
Трейты-расширения могут использоваться библиотеками, чтобы отличать стабильные методы от экспериментальных.
Стабильные методы являются частью определения трейта.
Экспериментальные методы предоставляются через трейт-расширение, определённый в другой библиотеке, с менее строгой политикой стабильности. Затем некоторые полезные методы «переносятся» в определение основного трейта, когда доказывают свою пользу и их дизайн дорабатывается.
-
Трейты-расширения можно использовать, чтобы разбить трейт, несовместимый с dyn, на две части:
- Совместимое с dyn ядро, ограниченное методами, которые удовлетворяют требованиям совместимости с dyn.
- Трейт-расширение, содержащий оставшиеся методы, которые несовместимы с dyn (например, методы с обобщённым параметром).
-
Конкретные типы, реализующие основной трейт, смогут вызывать все методы благодаря обобщённой реализации для трейта-расширения. Объекты трейтов (
dyn CoreTrait) смогут вызывать все методы основного трейта, а также те методы трейта-расширения, которые не требуютSelf: Sized.