Дополнение В: Выводимые типажи
Во многих частях книги мы обсуждали атрибут derive
, которые Вы могли применить к объявлению структуры или перечисления. Атрибут derive
генерирует код по умолчанию для реализации типажа, который вы указали в derive
.
В этом дополнении, мы расскажем про все типажи, которые вы можете использовать в атрибуте derive
. Каждая секция содержит:
- Операции и методы, добавляемые типажом
- Как представлена реализация типажа через
derive
- Что реализация типажа рассказывает про тип
- Условия, в которых разрешено или запрещено реализовывать типаж
- Примеры ситуаций, которые требуют наличие типажа
Если Вам понадобилось поведение отличное от поведения при реализации через derive
, обратитесь к документации по стандартной библиотеке чтобы узнать как вручную реализовать типаж.
Перечисленные здесь типажи являются единственными, определёнными стандартной библиотекой, которые могут быть реализованы в ваших типах с помощью derive
. Другие типажи, определённые в стандартной библиотеке, не имеют ощутимого поведения по умолчанию, поэтому вам решать, как реализовать их для достижения ваших целей.
Пример типажа, который нельзя реализовать через derive - Display
, который обрабатывает форматирование для конечных пользователей. Вы всегда должны сами рассмотреть лучший способ для отображения типа конечному пользователю. Какие части типа должны быть разрешены для просмотра конечному пользователю? Какие части они найдут подходящими? Какой формат вывода для них будет самым подходящим? Компилятор Rust не знает ответы на эти вопросы, поэтому он не может подобрать подходящее стандартное поведение.
Список типов, реализуемых через derive, в этом дополнении не является исчерпывающим: библиотеки могут реализовывать derive
для их собственных типажей, составляя свои списки типажей, которые Вы можете использовать с помощью derive
. Реализация derive
включает в себя использование процедурных макросов, которые были рассмотрены в разделе "Макросы" главы 19.
Debug
для отладочного вывода
Типаж Debug
включает отладочное форматирование в форматируемых строках, которые вы можете указать с помощью :?
внутри {}
фигурных скобок.
Типаж Debug
позволяет Вам напечатать объекты типа с целью отладки, поэтому Вы и другие программисты, использующие Ваш тип, смогут проверить объект в определённой точке выполнения программы.
Типаж Debug
обязателен в некоторых случаях. Например, при использовании макроса assert_eq!
. Этот макрос печатает значения входных аргументов, если они не совпадают. Это позволяет программистам увидеть, почему эти объекты не равны.
PartialEq
и Eq
для сравнения равенства
Типаж PartialEq
позволяет Вам сравнить объекты одного типа на эквивалентность, и включает для них использование операторов ==
и !=
.
Использование PartialEq
реализует метод eq
. Когда PartialEq
используют для структуры, два объекта равны если равны все поля объектов, и объекты не равны, если хотя бы одно поле отлично. Когда используется для перечислений, каждый вариант равен себе, и не равен другим вариантам.
Типаж PartialEq
обязателен в некоторых случаях. Например для макроса assert_eq!
, где необходимо сравнивать два объекта одного типа на эквивалентность.
Типаж Eq
не имеет методов. Он сигнализирует что каждое значение аннотированного типа равно самому себе. Типаж Eq
может быть применён только для типов реализующих типаж PartialEq
, хотя не все типы, которые реализуют PartialEq
могут реализовывать Eq
. Примером являются числа с плавающей запятой: реализация чисел с плавающей запятой говорит, что два экземпляра со значениями не-число (NaN
) не равны друг другу.
Типаж Eq
необходим в некоторых случаях. Например, для ключей в HashMap<K, V>
. Поэтому HashMap<K, V>
может сказать, что два ключа являются одним и тем же.
PartialOrd
и Ord
для сравнения порядка
Типаж PartialOrd
позволяет Вам сравнить объекты одного типа с помощью сортировки. Тип, реализующий PartialOrd
может использоваться с операторами <
, >
, <=
, и >=
. Вы можете реализовать типаж PartialOrd
только для типов, реализующих PartialEq
.
Использование PartialOrd
реализует метод partial_cmp
, который возвращает Option<Ordering>
который является None
когда значения не выстраивают порядок. Примером значения, которое не может быть упорядочено, не являются числом (NaN
) значение с плавающей запятой. Вызов partial_cmp
с любым числом с плавающей запятой и значением NaN
вернёт None
.
Когда используется для структур, PartialOrd
сравнивает два объекта путём сравнения значений каждого поля в порядке, в котором поля объявлены в структуре. Когда используется для перечислений, то варианты перечисления объявленные ранее будут меньше чем варианты объявленные позже.
Например, типаж PartialOrd
может потребоваться для метода gen_range
из rand
крейта который генерирует случайные значения в заданном диапазоне (который определён выражением диапазона).
Типаж Ord
позволяет знать, для двух значений аннотированного типа всегда будет существовать валидный порядок. Типаж Ord
реализовывает метод cmp
, который возвращает Ordering
а не Option<Ordering>
потому что валидный порядок всегда будет существовать. Вы можете применить типаж Ord
только для типов, реализовывающих типаж PartialOrd
и Eq
(Eq
также требует PartialEq
). При использовании на структурах или перечислениях, cmp
имеет такое же поведение, как и partial_cmp
вPartialOrd
.
Типаж Ord
необходим в некоторых случаях. Например, сохранение значений в BTreeSet<T>
, типе данных, который хранит информацию на основе порядка отсортированных данных.
Clone
и Copy
для дублирования значений
Типаж Clone
позволяет вам явно создать глубокую копию значения, а также процесс дублирования может вызывать специальный код и копировать данные с кучи. Более детально про Clone
смотрите в секции "Способы взаимодействия переменных и данных: клонирование" в разделе 4.
Использование Clone
реализует метод clone
, который в случае реализации на всем типе, вызывает clone
для каждой части данных типа. Это подразумевает, что все поля или значения в типе также должны реализовывать Clone
для использования Clone
.
Типаж Clone
необходим в некоторых случаях. Например, для вызова метода to_vec
для среза. Срез не владеет данными, содержащимися в нем, но вектор значений, возвращённый из to_vec
должен владеть этими объектами, поэтому to_vec
вызывает clone
для всех данных. Таким образом, тип хранящийся в срезе, должен реализовывать Clone
.
Типаж Copy
позволяет дублировать значения копируя только данные, которые хранятся на стеке, произвольный код не требуется. Смотрите секцию "Стековые данные: Копирование" в разделе 4 для большей информации о Copy
.
Типаж Copy
не содержит методов для предотвращения перегрузки этих методов программистами, иначе бы это нарушило соглашение, что никакой произвольный код не запускается. Таким образом все программисты могут предполагать, что копирование значений будет происходить быстро.
Вы можете вывести Copy
для любого типа все части которого реализуют Copy
. Тип который реализует Copy
должен также реализовывать Clone
, потому что тип реализующий Copy
имеет тривиальную реализацию Clone
который выполняет ту же задачу, что и Copy
.
Типаж Copy
нужен очень редко; типы, реализовывающие Copy
имеют небольшую оптимизацию, то есть для него не нужно вызывать метод clone
, который делает код более кратким.
Все, что вы делаете с Copy
можно также делать и с Clone
, но код может быть медленнее и требовать вызов метода clone
в некоторых местах.
Hash
для превращения значения в значение фиксированного размера
Типаж Hash
позволяет превратить значение произвольного размера в значение фиксированного размера с использованием хеш-функции. Использование Hash
реализует метод hash
. При реализации через derive, метод hash
комбинирует результаты вызова hash
на каждой части данных типа, то есть все поля или значения должны реализовывать Hash
для использования Hash
с помощью derive.
Типаж Hash
необходим в некоторых случаях. Например, для хранения ключей в HashMap<K, V>
, для их более эффективного хранения.
Default
для значений по умолчанию
Типаж Default
позволяет создавать значение по умолчанию для типа. Использование Default
реализует функцию default
. Стандартная реализация метода default
вызовет функцию default
на каждой части данных типа, то есть для использования Default
через derive, все поля и значения типа данных должны также реализовывать Default
.
Функция Default::default
часто используется в комбинации с синтаксисом обновления структуры, который мы обсуждали в секции "Создание экземпляра структуры из экземпляра другой структуры с помощью синтаксиса обновления структуры" главы 5. Вы можете настроить несколько полей для структуры, а для остальных полей установить значения с помощью ..Default::default()
.
Типаж Default
необходим в некоторых случаях. Например, для метода unwrap_or_default
у типа Option<T>
. Если значение Option<T>
будет None
, метод unwrap_or_default
вернёт результат вызова функции Default::default
для типа T
, хранящегося в Option<T>
.