Как Rust трактует impl Add for T, если у трейта Add параметр Rhs имеет значение по умолчанию?

Как Rust трактует impl Add for T, если у трейта Add параметр Rhs имеет значение по умолчанию?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

impl Add for T означает реализацию Add<T> for T: параметр Rhs берётся равным Self, потому что в трейте Add для него задано значение по умолчанию. Это не запрещает отдельным реализациям использовать другой тип правого операнда — тогда нужно явно указать Rhs.

Исторический контекст

Параметр Rhs позволяет описывать операции между разными типами, например сложение вектора и скаляра. Значение по умолчанию Rhs = Self сохраняет удобный и привычный случай бинарной операции между одинаковыми типами, не заставляя каждый раз явно указывать правый операнд.

Постановка проблемы

Без понимания значения по умолчанию легко ошибочно считать, что Add всегда принимает два одинаковых типа. Это приводит либо к неверному чтению существующего кода, либо к избыточным ограничениям API, который на самом деле может поддерживать смешанные операции.

Подробное решение

У Add есть параметр типа Rhs и ассоциированный тип Output. Если Rhs не указан в объявлении реализации, Rust подставляет Self. Поэтому запись impl Add for Meter семантически соответствует impl Add<Meter> for Meter.

Значение по умолчанию действует только там, где параметр не указан явно. Оно не является глобальным ограничением трейта и не мешает реализации Add<Distance> for Meter. Тип результата независимо задаётся через Output.

use std::ops::Add; struct Meter(i32); struct Offset(i32); impl Add for Meter { type Output = Meter; fn add(self, rhs: Meter) -> Meter { Meter(self.0 + rhs.0) } } impl Add<Offset> for Meter { type Output = Meter; fn add(self, rhs: Offset) -> Meter { Meter(self.0 + rhs.0) } }

Первая реализация принимает Meter, поскольку Rhs опущен и равен Self. Вторая явно задаёт Offset как правый операнд. Для generic-кода это также означает, что bound T: Add проверяет именно Add<T>, а для другого правого операнда требуется явно написать соответствующий параметр.

Ситуация из практики

Библиотека моделирования единиц измерения хочет поддержать сложение двух расстояний и добавление поправки. Вариант с единственной реализацией Add удобен для одинаковых типов, но не позволяет выразить операцию с поправкой; вариант с явным параметром Rhs для каждого случая точнее, но делает объявления более многословными.

Выбранное решение — оставить Rhs = Self для основного сценария и добавить отдельные реализации с явным Rhs для смешанных операций. В результате обычное сложение остаётся лаконичным, а типы правых операндов и результат проверяются статически без перегрузки через динамическую диспетчеризацию.

Что кандидаты часто упускают

  1. Означает ли Rhs = Self, что Add навсегда работает только с одинаковыми типами?

Нет. Это лишь значение по умолчанию при опущенном параметре. Реализация может явно указать другой Rhs, поэтому Meter может складываться как с Meter, так и с Offset.

  1. Определяет ли Rhs тип результата операции?

Нет. Rhs определяет тип правого операнда, а результат задаётся ассоциированным типом Output. Например, Matrix + Matrix может возвращать Matrix, а операция между двумя типами может возвращать отдельный тип результата.

  1. Что проверит bound T: Add, если нужен правый операнд другого типа?

T: Add использует значение по умолчанию и означает наличие реализации Add<T> for T. Для правого операнда R нужен bound с явным параметром — T: Add<R>. Иначе компилятор не получит гарантии, что операция между T и R существует.