Трейты-расширения
Иногда бывает желательно расширить внешние типы новыми методами. Например, позволить коду проверять, является ли строка палиндромом, с помощью синтаксиса вызова метода: s.is_palindrome().
Может показаться естественным обратиться к блоку impl:
// Copyright 2025 Google LLC
// SPDX-License-Identifier: Apache-2.0
// 🛠️❌
impl str {
pub fn is_palindrome(&self) -> bool {
self.chars().eq(self.chars().rev())
}
}
Однако компилятор Rust этого не допустит. Но обойти это ограничение можно с помощью паттерна «трейт-расширение».
-
Элемент Rust (будь то трейт или тип) называют:
- внешним, если он не определён в текущем крейте
- локальным, если он определён в текущем крейте Это различие имеет важные последствия для правил согласованности и правила сироты, с которыми мы разберёмся в этом разделе курса.
-
Скомпилируйте пример, чтобы показать ошибку компилятора.
Обратите внимание, как сообщение об ошибке компилятора подталкивает к паттерну трейта-расширения.
-
Объясните, что многие ограничения системы типов Rust направлены на предотвращение неоднозначности.
Что произойдёт, если разрешить определять новые собственные методы для внешних типов? Разные крейты в дереве зависимостей могли бы в итоге определить разные методы с одним и тем же именем для одного и того же внешнего типа.
Как только появляется место для неоднозначности, должен быть способ её устранить. Если устранение неоднозначности происходит неявно, это может привести к неожиданному поведению. Если же оно происходит явно, это может увеличить когнитивную нагрузку на разработчиков, которые читают ваш код.
Кроме того, каждый раз, когда крейт определяет новый собственный метод для внешнего типа, это может вызвать ошибки компиляции в вашем коде, поскольку вас может вынудить к явному устранению неоднозначности.
Rust решил вовсе избежать этой проблемы, запретив определять новые собственные методы для внешних типов.
-
Другие языки (например, Kotlin, C#, Swift) позволяют добавлять методы к существующим типам, которые часто называют «методами расширения». Это приводит к другим компромиссам с точки зрения возможной неоднозначности и необходимости глобальных рассуждений.