Додаток 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 і EqEq вимагає 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