Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Приложение C: Типажи, для которых можно использовать derive

В разных местах книги мы обсуждали атрибут derive, который можно применять к определению структуры или перечисления. Атрибут derive генерирует код, реализующий типаж с использованием его стандартной реализации для типа, который вы пометили с помощью синтаксиса derive.

В этом приложении мы приводим справочник по всем типажам стандартной библиотеки, которые можно использовать с derive. Каждый раздел охватывает:

  • Какие операторы и методы станут доступны при использовании этого типажа
  • Что делает реализация типажа, предоставляемая derive
  • Что означает реализация типажа для данного типа
  • Условия, при которых реализация типажа разрешена или запрещена
  • Примеры операций, требующих наличия типажа

Если вам нужно поведение, отличное от того, которое предоставляет атрибут derive, обратитесь к документации стандартной библиотеки для каждого типажа, чтобы узнать, как реализовать его вручную.

Перечисленные здесь типажи — единственные, определённые стандартной библиотекой, которые можно реализовать для ваших типов с помощью derive. Другие типажи, определённые в стандартной библиотеке, не имеют разумного поведения по умолчанию, поэтому реализация их зависит от вас и должна соответствовать вашим задачам.

Примером типажа, который нельзя получить через derive, является Display, который отвечает за форматирование для конечных пользователей. Вы всегда должны задумываться о подходящем способе отображения типа для конечного пользователя. Какие части типа пользователь должен видеть? Какие части будут для него релевантны? В каком формате данные будут наиболее полезны? У компилятора Rust нет такого понимания, поэтому он не может предоставить вам подходящее поведение по умолчанию.

Список типажей, которые можно получить через derive в этом приложении, не является исчерпывающим: библиотеки могут реализовывать derive для своих собственных типажей, поэтому список типажей, с которыми можно использовать derive, по сути открыт. Реализация derive предполагает использование процедурного макроса, который рассматривается в разделе “Макросы” главы 20.

Debug для вывода, предназначенного для программистов

Типаж Debug включает форматирование для отладки в строках формата, которое указывается добавлением :? внутри заполнителей {}.

Типаж Debug позволяет выводить экземпляры типа для целей отладки, чтобы вы и другие программисты, использующие ваш тип, могли inspecting экземпляр в определённой точке выполнения программы.

Типаж Debug требуется, например, при использовании макроса assert_eq!. Этот макрос выводит значения экземпляров, переданных в качестве аргументов, если проверка на равенство завершилась неудачей, чтобы программисты могли понять, почему два экземпляра не равны.

PartialEq и Eq для сравнений на равенство

Типаж PartialEq позволяет сравнивать экземпляры типа для проверки на равенство и включает использование операторов == и !=.

Получение PartialEq через derive реализует метод 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 через derive реализует метод partial_cmp, который возвращает Option<Ordering>, который будет None, когда переданные значения не образуют порядок. Примером значения, не образующего порядок, даже если большинство значений этого типа можно сравнить, является значение «не число» (NaN) для чисел с плавающей запятой. Вызов partial_cmp с любым числом с плавающей запятой и значением NaN для чисел с плавающей запятой вернёт None.

При применении к структурам PartialOrd сравнивает два экземпляра, сравнивая значение каждого поля в порядке, в котором поля определены в структуре. При применении к перечислениям варианты перечисления, объявленные раньше в определении перечисления, считаются меньшими, чем варианты, перечисленные позже.

Типаж PartialOrd требуется, например, для метода gen_range из крейта rand, который генерирует случайное значение в диапазоне, заданном выражением диапазона.

Типаж Ord позволяет знать, что для любых двух значений аннотированного типа всегда существует допустимый порядок. Типаж Ord реализует метод cmp, который возвращает Ordering вместо Option<Ordering>, поскольку допустимый порядок всегда возможен. Типаж Ord можно применять только к типам, которые также реализуют PartialOrd и EqEq требует PartialEq). При получении через derive для структур и перечислений cmp ведёт себя так же, как реализация partial_cmp для PartialOrd.

Примером, когда требуется Ord, является хранение значений в BTreeSet<T>, структуре данных, которая хранит данные на основе порядка сортировки значений.

Clone и Copy для дублирования значений

Типаж Clone позволяет явно создавать глубокую копию значения, и процесс дублирования может включать выполнение произвольного кода и копирование данных в куче. Подробнее о Clone см. в разделе “Переменные и данные: взаимодействие с Clone в главе 4.

Получение Clone через derive реализует метод clone, который при реализации для всего типа вызывает clone для каждой части типа. Это означает, что все поля или значения в типе также должны реализовывать Clone, чтобы можно было получить Clone через derive.

Примером, когда требуется Clone, является вызов метода to_vec на срезе. Срез не владеет экземплярами типа, которые он содержит, но вектор, возвращаемый из to_vec, должен владеть своими экземплярами, поэтому to_vec вызывает clone для каждого элемента. Следовательно, тип, хранящийся в срезе, должен реализовывать Clone.

Типаж Copy позволяет дублировать значение путём простого копирования битов, хранящихся в стеке; произвольный код не требуется. Подробнее о Copy см. в разделе “Данные только в стеке: Copy в главе 4.

Типаж Copy не определяет никаких методов, чтобы предотвратить перегрузку этих методов программистами и нарушение предположения о том, что не выполняется произвольный код. Таким образом, все программисты могут предполагать, что копирование значения будет очень быстрым.

Типаж Copy можно получить через derive для любого типа, все части которого реализуют Copy. Тип, реализующий Copy, должен также реализовывать Clone, поскольку тип, реализующий Copy, имеет тривиальную реализацию Clone, которая выполняет ту же задачу, что и Copy.

Типаж Copy требуется редко; типы, реализующие Copy, имеют доступные оптимизации, что означает, что вам не нужно вызывать clone, что делает код более лаконичным.

Всё, что возможно с Copy, можно также выполнить с Clone, но код может быть медленнее или должен использовать clone в определённых местах.

Hash для отображения значения в значение фиксированного размера

Типаж Hash позволяет взять экземпляр типа произвольного размера и отобразить этот экземпляр в значение фиксированного размера с помощью хэш-функции. Получение Hash через derive реализует метод hash. Реализация метода hash, полученная через derive, комбинирует результат вызова hash для каждой части типа, что означает, что все поля или значения должны также реализовывать Hash, чтобы можно было получить Hash через derive.

Примером, когда требуется Hash, является хранение ключей в HashMap<K, V> для эффективного хранения данных.

Default для значений по умолчанию

Типаж Default позволяет создать значение по умолчанию для типа. Получение Default через derive реализует функцию default. Реализация функции default, полученная через derive, вызывает функцию default для каждой части типа, что означает, что все поля или значения в типе должны также реализовывать Default, чтобы можно было получить Default через derive.

Функция Default::default обычно используется в сочетании с синтаксисом обновления структуры, рассмотренным в разделе “Создание экземпляров из других экземпляров с помощью синтаксиса обновления структуры” в главе 5. Вы можете настроить несколько полей структуры, а затем установить и использовать значение по умолчанию для остальных полей, используя ..Default::default().

Типаж Default требуется, когда вы используете метод unwrap_or_default для экземпляров Option<T>, например. Если Option<T> равен None, метод unwrap_or_default вернёт результат Default::default для типа T, хранящегося в Option<T>.