Ваша обёртка должна реализовывать трейт только при выполнении bound внутренним типом. Как Rust определяет доступность такой реализации при вызове метода?
Rust рассматривает условную реализацию только тогда, когда в конкретном месте использования доказаны все её bounds. Если внутренний тип обёртки не удовлетворяет требуемому ограничению, соответствующей реализации трейта для обёртки не существует с точки зрения данного кода, поэтому методы этого трейта недоступны.
Условные реализации появились как часть системы обобщений, чтобы библиотеки могли описывать свойства составных типов без ручного кода для каждого конкретного типа. Например, контейнер можно сделать клонируемым только тогда, когда клонируем его элемент.
Такой подход позволяет переносить гарантии из типов-компонентов в тип-обёртку на этапе компиляции, не добавляя проверки во время выполнения. Он также сохраняет статическую диспетчеризацию и согласуется с правилами когерентности Rust.
Нужно отличать существование типа от существования его условной реализации. Сам тип обёртки может быть корректным для любого параметра, но конкретный трейт для него будет реализован только при выполнении указанного bound.
Если generic-код пытается вызвать метод без достаточных ограничений, компилятор не может предположить, что условная реализация существует. Добавление bound в месте вызова делает это требование явным и проверяемым.
Рассмотрим минимальный пример:
Реализация Describe for Boxed<T> является условной. При проверке show параметр T уже ограничен Describe, поэтому компилятор выводит, что Boxed<T> тоже реализует Describe, и разрешает вызов метода.
Без bound T: Describe функция не скомпилировалась бы: для произвольного T тело Boxed<T> не обязано иметь реализацию Describe. Ограничение должно находиться там, где оно необходимо: в параметрах функции, другой реализации, методе или associated type.
Компилятор не выбирает условную реализацию динамически. Он проверяет применимость реализации статически, а для каждого конкретного набора типов обычно строит специализированный код. Если одновременно подходят несколько реализаций, правила когерентности и отсутствия пересечений не позволяют оставить неоднозначность.
Условная реализация не меняет сам тип Boxed<T> и не добавляет runtime-проверку. Она меняет множество trait-реализаций, доступных для конкретной комбинации типов.
В библиотеке есть обёртка Cache<T>. Требуется предоставить ей Serialize только при сериализуемости T.
Вариант с безусловной реализацией потребовал бы определить, как сериализовать любой T, что невозможно без дополнительного ограничения или специального формата. Runtime-проверка была бы менее типобезопасной и не устранила бы проблему отсутствия общей стратегии сериализации.
Условная реализация Serialize for Cache<T> where T: Serialize точно отражает контракт: клиент получает сериализацию только для поддерживаемых элементов. Минус — generic-функции, работающие с Cache<T>, должны явно или косвенно иметь тот же bound; это увеличивает количество ограничений в API, но делает ошибки локальными и понятными.
Нет. Успешное создание Boxed<T> доказывает только корректность самого типа обёртки. Для вызова метода Describe компилятору нужно доказательство T: Describe именно в текущей области проверки.
Нет. Она применяется только к тем параметрам, которые удовлетворяют её условиям. Кроме того, нельзя добавить конфликтующую реализацию позже, если правила когерентности допускают пересечение с уже существующей условной реализацией.
Обычно нет в том же смысле. Rust не поддерживает общий runtime-вопрос «реализует ли произвольный T этот трейт» как замену статическому bound. Для динамического выбора используют trait object или другую явно спроектированную абстракцию, но это меняет модель диспетчеризации, ограничения object safety и часто стоимость вызова.