Как сравнить два значения enum в Rust без использования match?
Использование derive-макроса PartialEq для сравнения enum
Да, в Rust можно сравнивать значения enum напрямую с помощью оператора ==. Для этого нужно просто добавить атрибут #[derive(PartialEq)] к вашему перечислению. PartialEq — это типаж (trait), который реализует операторы сравнения == и !=.
Вот как это работает на практике с вашим примером:
#[derive(Debug, PartialEq)] // Добавляем PartialEq
enum OrderStatus {
Pending,
Processing,
Shipped,
Delivered,
Cancelled,
}
fn main() {
let status = OrderStatus::Pending;
if status == OrderStatus::Pending {
println!("Заказ ожидает обработки");
}
if status != OrderStatus::Shipped {
println!("Заказ ещё не отправлен");
}
}
Почему это работает?
Когда вы пишете #[derive(PartialEq)], компилятор Rust автоматически генерирует реализацию типажа PartialEq для вашего enum. Эта реализация сравнивает варианты по их дискриминантам (числовым значениям, которые Rust присваивает каждому варианту). Если варианты совпадают, то == возвращает true.
Важные ограничения
- Сравнение возможно только для enum без данных — если ваши варианты содержат данные (например,
Value(i32)), тоPartialEqбудет сравнивать и эти данные тоже. Это работает, но требует, чтобы все типы данных внутри вариантов тоже реализовывалиPartialEq. - Для enum с данными нужно явно указывать
PartialEq— если у вас естьenum Option<T>или свой enum с полями, тоderiveавтоматически добавитPartialEqтолько если все поля его реализуют. - Не путайте с
Eq— есть ещё типажEq, который является надмножествомPartialEq.Eqгарантирует рефлексивность (a == a всегда true). Для обычных сравнений достаточноPartialEq.
Пример с данными в вариантах
#[derive(Debug, PartialEq)]
enum Message {
Text(String),
Number(i32),
Quit,
}
fn main() {
let msg1 = Message::Text("hello".to_string());
let msg2 = Message::Text("hello".to_string());
let msg3 = Message::Number(42);
println!("msg1 == msg2: {}", msg1 == msg2); // true
println!("msg1 == msg3: {}", msg1 == msg3); // false
}
Когда использовать match вместо ==?
Хотя == удобен для простых проверок, match остаётся более мощным инструментом:
- Извлечение данных —
matchпозволяет получить данные из варианта (например,Message::Text(text) => println!("{}", text)). - Обработка нескольких вариантов —
matchгарантирует, что вы обработали все возможные случаи (исчерпывающее сопоставление). - Условные проверки — с помощью
if letможно проверить один конкретный вариант и извлечь данные:if let Message::Text(text) = msg { ... }.
Таким образом, для простого сравнения на равенство используйте #[derive(PartialEq)] и оператор ==. Это лаконично, читаемо и эффективно.
Альтернативный подход: использование if let для сопоставления с образцом
Если вам не нужно полное сравнение на равенство, а достаточно проверить только один конкретный вариант, if let может быть более идиоматичным решением, чем match или ==:
enum OrderStatus {
Pending,
Processing,
Shipped,
}
fn main() {
let status = OrderStatus::Pending;
if let OrderStatus::Pending = status {
println!("Заказ ожидает обработки");
}
}
Преимущества if let:
- Не требует добавления
#[derive(PartialEq)]— работает с любым enum. - Позволяет извлечь данные из варианта:
if let Some(value) = option { ... }. - Более явно показывает намерение проверить один конкретный случай.
Недостаток: если вам нужно сравнить два значения enum (например, status == OrderStatus::Pending), if let не подходит — он проверяет, соответствует ли значение определённому образцу, а не сравнивает два произвольных значения.
Выбор между == и if let зависит от задачи. Для простой проверки на равенство — PartialEq + ==; для проверки с извлечением данных — if let.