Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Расширение других трейтов

Как и в случае с типами, иногда желательно расширить внешние трейты. В частности, чтобы добавить новые методы всем типам, реализующим заданный трейт.

// 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.