Сравните параметр impl Trait в аргументе функции с именованным generic-параметром с тем же bound: какое ограничение выразительности теряется?
impl Trait в позиции аргумента означает отдельный неявный обобщённый параметр и обычно использует статическую диспетчеризацию. Но этому параметру нельзя дать имя и сослаться на него в других частях сигнатуры: поэтому нельзя выразить связь между несколькими аргументами или между аргументом и возвращаемым типом.
Именованный параметр T с тем же bound позволяет повторно использовать один и тот же тип, задавать отношения между параметрами и при необходимости явно указывать тип при вызове.
Обобщённые функции позволяют писать код один раз для множества типов, сохраняя проверку bounds на этапе компиляции и избегая обязательных накладных расходов динамической диспетчеризации. Однако явная запись generic-параметров бывает избыточной, когда тип используется только в одном аргументе.
impl Trait в аргументе решает именно эту задачу: скрывает имя параметра, сохраняя обобщённость и статическую диспетчеризацию. Это средство упрощения интерфейса, а не переход к dyn Trait.
Рассмотрим функцию, принимающую два значения, реализующих один трейт. Запись с двумя вхождениями impl Trait не означает, что типы аргументов совпадают: каждое вхождение представляет свой независимый скрытый параметр.
Если API требует одинаковый конкретный тип, использование impl Trait в обоих аргументах ослабит контракт. В результате компилятор примет вызов с двумя разными типами, хотя реализация функции или её семантика может рассчитывать на их совпадение.
В позиции аргумента impl Trait концептуально эквивалентен отдельному анонимному generic-параметру. Для каждого вызова компилятор выводит конкретный тип, проверяет его bounds и обычно создаёт специализированную версию функции; виртуальная таблица dyn Trait здесь не используется.
В two параметры могут иметь разные типы, например Html и Json, если оба реализуют Render. В same оба аргумента обязаны иметь один и тот же тип T.
Именованный параметр также можно использовать в возвращаемом типе, других bounds и связанных ограничениях. Например, он позволяет выразить, что возвращаемое значение имеет тот же тип, что и входной аргумент; анонимный параметр impl Trait нельзя повторно обозначить в сигнатуре.
Есть и различие в управлении вызовом: именованный generic-параметр можно явно указать через generic-аргументы функции, тогда как скрытый параметр аргумента с impl Trait нельзя адресовать по имени. В большинстве случаев вывод типов делает это различие незаметным.
Оба варианта имеют компромисс: статическая диспетчеризация даёт оптимизацию и проверку на этапе компиляции, но может увеличить размер бинарника из-за мономорфизации. Если нужен единый машинный код для произвольных реализаций и возможность подменять тип во время выполнения, следует рассматривать dyn Trait, но это уже другой контракт и механизм диспетчеризации.
В библиотеке форматирования сначала объявили функцию с двумя аргументами impl Render. Она принимала документы разных конкретных типов, хотя алгоритм сравнения корректен только для двух значений одного представления.
Рассматривались два варианта. Оставить impl Render было проще для короткой сигнатуры, но оно не выражало требование одинакового типа. Перейти к dyn Render позволяло работать через общий интерфейс во время выполнения, однако добавляло виртуальную диспетчеризацию, требования к object safety и часто усложняло владение данными.
Выбрали именованный T: Render для обоих аргументов. Контракт стал проверяться компилятором, сохранилась статическая диспетчеризация, а попытка передать разные типы стала ошибкой ещё до выполнения программы.
impl Trait в одной сигнатуре?Нет. Каждый параметр impl Trait создаёт независимый анонимный generic-параметр. Поэтому два аргумента могут иметь разные конкретные типы, если каждый удовлетворяет указанному bound.
Чтобы потребовать совпадение типов, нужно использовать один именованный параметр, например T, в обоих местах сигнатуры. Это отличие является частью контракта API, а не только разницей в стиле записи.
impl Trait динамическую диспетчеризацию?Нет. В аргументе impl Trait тип выбирается при компиляции конкретного вызова, а операции над ним обычно мономорфизируются. Вызов через dyn Trait, напротив, хранит указатель на значение и виртуальную таблицу и выбирает реализацию во время выполнения.
Поэтому impl Trait обычно сохраняет преимущества статической диспетчеризации, но может привести к нескольким специализированным копиям функции. dyn Trait уменьшает зависимость кода от конкретного типа, зато имеет стоимость косвенного вызова и ограничения трейтовых объектов.
impl Trait?Нет, скрытому параметру impl Trait в аргументе нельзя обратиться по имени через generic-аргументы. Тип обычно выводится из переданного значения.
У именованного параметра T такая возможность есть: его можно явно задать при вызове, если это требуется для устранения неоднозначности или управления выводом. Кроме того, имя T можно использовать в возвращаемом типе и других bounds, чего анонимная форма не позволяет.