Трейты равенства
Часть раздела Object Oriented Programming путешествия по Rust на Coddy — урок 38 из 61.
Сравнение двух экземпляров пользовательской структуры с помощью == не работает в Rust по умолчанию. Компилятор не знает, как определить, равны ли две структуры — вам нужно указать это, реализовав типаж PartialEq.
Как и в случае с Debug и Copy, вы можете автоматически реализовать PartialEq:
#[derive(PartialEq)]
struct Coordinate {
x: i32,
y: i32,
}
let a = Coordinate { x: 5, y: 10 };
let b = Coordinate { x: 5, y: 10 };
let c = Coordinate { x: 3, y: 7 };
println!("{}", a == b); // true
println!("{}", a == c); // false
При автоматической реализации PartialEq сравнивает каждое поле структуры. Два экземпляра равны только в том случае, если все их соответствующие поля совпадают.
Вы также встретите Eq, который является маркерным типажом, указывающим на полное равенство — это означает, что каждое значение равно самому себе. Большинство типов удовлетворяют этому условию, но числа с плавающей запятой — нет (потому что NaN != NaN). Для структур с целочисленными полями вы можете безопасно реализовать (derive) оба типажа:
#[derive(PartialEq, Eq)]
struct Coordinate {
x: i32,
y: i32,
}
Eq не имеет собственных методов — он просто сигнализирует о том, что тип обладает свойством рефлексивного равенства. Некоторые возможности стандартной библиотеки требуют Eq в дополнение к PartialEq, поэтому для подходящих типов принято реализовывать (derive) оба трейта вместе.
Задание
ЛегкоДавайте создадим систему сопоставления цветов, которая демонстрирует, как типажи PartialEq и Eq позволяют сравнивать экземпляры структур!
Вы создадите два файла для организации логики сравнения цветов:
color.rs: Определите публичную структуруColorс тремя публичными полями:red,greenиblue(все типаu8). Реализуйте (derive) оба типажаPartialEqиEq, чтобы два цвета можно было сравнивать с помощью==. При автоматической реализации Rust будет считать два цвета равными только в том случае, если все три RGB-компонента совпадают точно.main.rs: Подключите модуль color и создайте два экземпляраColor, используя предоставленные входные данные. Сравните их с помощью==и выведите результат, совпадают они или нет.
Ваш вывод должен указывать, являются ли два цвета одинаковыми:
Color 1: rgb({r1}, {g1}, {b1})
Color 2: rgb({r2}, {g2}, {b2})
Colors match: {true/false}Например, при входных данных 255, 128, 0, 255, 128, 0:
Color 1: rgb(255, 128, 0)
Color 2: rgb(255, 128, 0)
Colors match: trueИ при входных данных 100, 100, 100, 200, 200, 200:
Color 1: rgb(100, 100, 100)
Color 2: rgb(200, 200, 200)
Colors match: falseВы получите шесть входных значений: RGB-компоненты для первого цвета (три значения), а затем RGB-компоненты для второго цвета (три значения). Обработайте каждое из них как u8.
Шпаргалка
Чтобы сравнивать пользовательские структуры с помощью ==, реализуйте типаж PartialEq, производя его автоматически (deriving):
#[derive(PartialEq)]
struct Coordinate {
x: i32,
y: i32,
}
let a = Coordinate { x: 5, y: 10 };
let b = Coordinate { x: 5, y: 10 };
println!("{}", a == b); // true
При автоматическом выводе PartialEq сравнивает все поля — два экземпляра считаются равными только в том случае, если все соответствующие поля совпадают.
Типаж Eq — это маркерный типаж, указывающий на полное равенство (каждое значение равно самому себе). Большинство типов удовлетворяют этому условию, за исключением чисел с плавающей запятой (потому что NaN != NaN). Для структур с целочисленными полями выводите оба типажа вместе:
#[derive(PartialEq, Eq)]
struct Coordinate {
x: i32,
y: i32,
}
У Eq нет методов — он сигнализирует о рефлексивном равенстве. Некоторые функции стандартной библиотеки требуют наличия Eq в дополнение к PartialEq.
Попробуйте сами
mod color;
use color::Color;
fn main() {
// Считать шесть значений RGB
let mut input = String::new();
std::io::stdin().read_line(&mut input).expect("Failed to read line");
let r1: u8 = input.trim().parse().expect("Invalid input");
input.clear();
std::io::stdin().read_line(&mut input).expect("Failed to read line");
let g1: u8 = input.trim().parse().expect("Invalid input");
input.clear();
std::io::stdin().read_line(&mut input).expect("Failed to read line");
let b1: u8 = input.trim().parse().expect("Invalid input");
input.clear();
std::io::stdin().read_line(&mut input).expect("Failed to read line");
let r2: u8 = input.trim().parse().expect("Invalid input");
input.clear();
std::io::stdin().read_line(&mut input).expect("Failed to read line");
let g2: u8 = input.trim().parse().expect("Invalid input");
input.clear();
std::io::stdin().read_line(&mut input).expect("Failed to read line");
let b2: u8 = input.trim().parse().expect("Invalid input");
// TODO: Создать два экземпляра Color, используя полученные значения
// TODO: Вывести Color 1 в формате: Color 1: rgb(r, g, b)
// TODO: Вывести Color 2 в формате: Color 2: rgb(r, g, b)
// TODO: Сравнить два цвета с помощью == и вывести: Colors match: {true/false}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Методы и поведение
Введение в блоки реализацииПараметр SelfИзменяемые методыАссоциированные функцииНесколько блоков реализацииЦепочки вызовов методовИтоги: действия с Rectangle4Проект: Виртуальный питомец
Определение питомцаКормление питомца2Инкапсуляция и модули
Основы модулейКлючевое слово PublicПриватные поляГеттерыСеттерыИтоги — Безопасный сейф5Обобщения
Обобщённые структурыОбобщённые методыНесколько обобщённых типовОбобщённые функцииПовторение — Координатная точка8Трейты как ограничения
Синтаксис ограничений трейтовМножественные ограниченияКонструкция whereВозврат типов с трейтамиИтоги — Обобщённый принтер11Паттерны проектирования в Rust
Паттерн NewtypeКомпозицияТрейт DropFrom и IntoИтоги — Мок-объект Smart Pointer3Продвинутые перечисления
Перечисления с даннымиМетоды в перечисленияхСопоставление вариантов с даннымиСнова об Option EnumИтоги — перечисление Shape6Определение трейтов
Что такое трейт?Реализация трейтовРеализации по умолчаниюПереопределение реализаций по умолчаниюТрейты с параметрамиИтоги — Media Player