Почему два параметра функции, записанные через impl Trait, не обязаны иметь один и тот же конкретный тип?
Каждое вхождение impl Trait в списке параметров обозначает отдельный неявный параметр типа. Поэтому два таких параметра могут получить разные конкретные типы, если каждый из них реализует требуемый Trait. Чтобы связать параметры одним типом, нужно объявить именованный параметр типа и использовать его повторно.
impl Trait появился как сокращённый способ выразить ограничение на тип, не раскрывая вызывающему лишние детали объявления. Такой синтаксис уменьшает шаблонный код, но не создаёт автоматически отношения равенства между несколькими параметрами.
В параметрах функции impl Trait сохраняет обобщённую семантику: вызывающий код выбирает конкретный тип для каждого отдельного параметра. Это отличается от ситуации, когда один именованный параметр типа используется в нескольких местах объявления.
Разработчик может ошибочно принять два одинаковых написания impl Trait за ссылку на один тип. Тогда функция неожиданно примет комбинацию разных реализаций трейта, хотя логика может требовать единого конкретного типа.
Обратная ошибка тоже возможна: замена независимых impl Trait на один параметр T чрезмерно ограничит API и запретит корректные вызовы с двумя разными типами.
В параметре функции запись impl Trait семантически близка к отдельному обобщённому параметру типа с ограничением Trait. Если запись встречается дважды, создаются два независимых параметра; совпадение их конкретных типов не требуется.
independent допускает разные типы для left и right, например структуру и перечисление, если оба реализуют Render. В same оба аргумента обязаны иметь один и тот же конкретный тип T.
Именованный параметр нужен также, когда тип требуется связать с возвращаемым значением, другим ограничением или несколькими позициями объявления. impl Trait удобнее для независимых параметров, но скрывает имя типа и не позволяет выразить связь между разными вхождениями.
Это не означает динамическую диспетчеризацию: в обычном параметре impl Trait используется статически выбранный конкретный тип и мономорфизация, как у обобщённой функции. Для общего динамического значения применяют dyn Trait, но это уже другая модель с динамическим вызовом и ограничениями объектной безопасности.
В API обработки данных есть функция, принимающая источник и форматтер. Источник может быть одним типом, а форматтер — другим, но оба реализуют общий трейт. Использование двух независимых impl Trait делает API компактным и не навязывает искусственное равенство типов.
Вариант с одним T проще для случаев, где оба аргумента действительно должны быть экземплярами одного типа, однако он отклонит разные корректные реализации. Вариант с dyn Trait допускает динамический выбор реализации, но усложняет модель вызова и может добавить стоимость динамической диспетчеризации.
Выбором будут два независимых impl Trait, если между аргументами нет требования одинакового типа. Это сохраняет статическую диспетчеризацию и не ограничивает композицию разных реализаций.
Дополнительный вопрос: эквивалентны ли два параметра impl Trait одному параметру T с ограничением Trait?
Нет. По ограничениям они похожи, но два вхождения impl Trait независимы. Один T, повторённый в двух параметрах, дополнительно требует, чтобы конкретные типы аргументов совпадали.
Дополнительный вопрос: можно ли использовать имя типа, скрытого за impl Trait в параметре, в типе результата?
Нет, потому что у этого типа нет доступного имени в сигнатуре. Если результат должен быть связан с типом аргумента, нужно объявить именованный параметр, например T, и использовать его и для параметра, и для результата.
Дополнительный вопрос: означает ли impl Trait в параметре передачу значения через динамический интерфейс?
Нет. Конкретный тип известен компилятору при мономорфизации, поэтому вызов обычно использует статическую диспетчеризацию. Динамическая диспетчеризация выражается через dyn Trait, обычно вместе со ссылкой или другим указателем на trait object.