Конструкция match
Простого if
/else
часто недостаточно, потому что нужно проверить больше, чем
два возможных варианта. Да и к тому же условия в else
часто становятся очень
сложными. Как же решить эту проблему?
В Rust есть ключевое слово match
, позволяющее заменить группы операторов
if
/else
чем-то более удобным. Смотрите:
let x = 5;
match x {
1 => println!("один"),
2 => println!("два"),
3 => println!("три"),
4 => println!("четыре"),
5 => println!("пять"),
_ => println!("что-то ещё"),
}
match
принимает выражение и выбирает одну из ветвей исполнения согласно его
значению. Каждая ветвь имеет форму значение => выражение
. Выражение ветви
вычисляется, когда значение данной ветви совпадает со значением, принятым
оператором match
(в данном случае, x
). Эта конструкция называется match
(сопоставление), потому что она выполняет сопоставление значения неким
«шаблонам». Глава «Шаблоны» описывает все шаблоны, которые можно
использовать в match
.
Так в чём же преимущества данной конструкции? Их несколько. Во-первых, ветви
match
проверяются на полноту. Видите последнюю ветвь, со знаком
подчёркивания (_
)? Если мы удалим её, Rust выдаст ошибку:
error: non-exhaustive patterns: `_` not covered
Другими словами, компилятор сообщает нам, что мы забыли сопоставить какие-то
значения. Поскольку x
— это целое число, оно может принимать разные значения —
например, 6
. Однако, если мы убираем ветвь _
, ни одна ветвь не совпадёт,
поэтому такой код не скомпилируется. _
— это «совпадение с любым значением».
Если ни одна другая ветвь не совпала, совпадёт ветвь с _
. Поскольку в примере
выше есть ветвь с _
, мы покрываем всё множество значений x
, и наша программа
скомпилируется.
match
также является выражением. Это значит, что мы можем использовать его в
правой части оператора let
или непосредственно как выражение:
let x = 5;
let numer = match x {
1 => "one",
2 => "two",
3 => "three",
4 => "four",
5 => "five",
_ => "something else",
};
Иногда с помощью match
можно удобно преобразовать значения одного типа в
другой.
Сопоставление с образцом для перечислений
Другой полезный способ использования match
— обработка возможных вариантов
перечисления:
enum Message {
Quit,
ChangeColor(i32, i32, i32),
Move { x: i32, y: i32 },
Write(String),
}
fn quit() { /* ... */ }
fn change_color(r: i32, g: i32, b: i32) { /* ... */ }
fn move_cursor(x: i32, y: i32) { /* ... */ }
fn process_message(msg: Message) {
match msg {
Message::Quit => quit(),
Message::ChangeColor(r, g, b) => change_color(r, g, b),
Message::Move { x: x, y: y } => move_cursor(x, y),
Message::Write(s) => println!("{}", s),
};
}
Как обычно, компилятор Rust проверяет полноту, поэтому в match
должна быть
ветвь для каждого варианта перечисления. Если какой-то вариант отсутствует,
программа не скомпилируется и вам придётся использовать _
.
Здесь мы не можем использовать обычный if
вместо match
, в отличие от кода,
который мы видели раньше. Но мы могли бы использовать [if let
]if-let — его
можно воспринимать как сокращённую форму записи match
.