Додаток C: Похідні Трейти
У різних місцях книги ми обговорювали атрибут derive
, який можна застосувати до визначення структури або енуму. Атрибут derive
генерує код, що реалізує трейт з власною реалізацією за замовчуванням для типу, який ви позначили за допомогою синтаксичної конструкції derive
.
У цьому додатку наведено перелік усіх трейтів стандартної бібліотеки, які ви можете застосовувати з derive
. Кожен розділ покриває:
- Які оператори та методи дозволить застосування цього трейту
- Що робить реалізація трейту, створена за допомогою
derive
- Що реалізація трейту позначає для типу
- Умови, за яких вам можна чи не можна реалізовувати трейт
- Приклади операцій, що вимагають цього трейту
Якщо ви хочете отримати поведінку, відмінну від наданої атрибутом derive
, зверніться до документації стандартної бібліотеки
для кожного трейту, щоб дізнатися подробиці, як реалізувати їх вручну.
Тут наведений повний список трейтів, визначених у стандартній бібліотеці, які можуть бути реалізовані для ваших типів за допомогою derive
. Інші трейти, визначені в стандартній бібліотеці, не мають притомної поведінки за замовчуванням, тому ви повинні реалізувати їх так, щоб вони мали сенс для досягнення вашої конкретної мети.
Приклад трейту, який не можна вивести, це Display
, що обробляє форматування для кінцевих користувачів. Ви завжди маєте продумати відповідний спосіб, як показати ваш тип кінцевому користувачеві. Які частини типу має кінцевий користувач право бачити? Які частини будуть для них актуальними? Який формат даних буде для них найбільш адекватним? Компілятор Rust не може цього знати, тож не може й забезпечити відповідну поведінку за замовчуванням.
Список вивідних трейтів, наданий у цьому додатку, не є вичерпним: бібліотеки можуть реалізувати 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
дозволяє явно створити глибоку копію значення, і процес дублікації може містити виконання довільного коду і копіювання даних у купі. Дивіться підрозділ “Як взаємодіють змінні з даними: клонування” Розділу 4 для додаткової інформації про Clone
.
Виведення 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
. Виведена реалізація методу hash
комбінує результати викликів hash
для кожної частини типу, що означає, що всі поля і значення також мають реалізовувати Hash
для виведення Hash
.
Приклад, коли потрібен Hash
, це зберігання ключів у HashMap<K, V>
, щоб ефективно зберігати дані.
Default
для Значень за Замовчуванням
Трейт Default
дозволяє вам створювати значення за замовчуванням для типу. Виведення Default
реалізовує функцію default
. Виведена реалізація функції default
викликає функцію default
для кожної частини типу, що означає, що всі поля або значення в типі також повинні реалізовувати Default
, щоб можна було вивести Default
.
Функція Default::default
зазвичай використовується у поєднанні з синтаксисом оновлення структури, про який ідеться в підрозділі "Створення екземплярів з інших екземплярів за допомогою синтаксису оновлення структур"
Розділу 5. Ви можете виставити кілька полів конструкції, а потім встановити і використати значення за замовчуванням для решти полів за допомогою ..Default::default()
.
Наприклад, трейт Default
необхідний, коли ви використовуєте метод unwrap_or_default
для екземплярів Option<T>
. Якщо Option<T>
має значення None
, метод unwrap_or_default
поверне результат Default::default
для типу T
, що знаходиться в Option<T>
.
ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax ch04-01-what-is-ownership.html#stack-only-data-copy ch04-01-what-is-ownership.html#ways-variables-and-data-interact-clone