Match в Rust
match сравнивает значение со списком образцов (patterns) и выполняет код той ветки, которая подходит. Если ты пользовался оператором switch в других языках, считай match его гораздо более мощной версией. А если не пользовался — ничего страшного: это одна из самых полезных конструкций, и её стоит выучить пораньше.
Простой match
Заголовок раздела «Простой match»fn main() { let day = 3;
match day { 1 => println!("Понедельник"), 2 => println!("Вторник"), 3 => println!("Среда"), 4 => println!("Четверг"), 5 => println!("Пятница"), _ => println!("Выходной"), }}Вывод:
Среда_ — это ветка-заглушка: она подходит для всего, что не перечислено выше. match требует обработать все возможные значения, поэтому _ обычно стоит в конце и покрывает «всё остальное».
Несколько значений сразу
Заголовок раздела «Несколько значений сразу»Несколько значений можно объединить в одну ветку через |:
fn main() { let day = 6;
match day { 1 | 2 | 3 | 4 | 5 => println!("Будний день"), 6 | 7 => println!("Выходной"), _ => println!("Такого дня нет"), }}Диапазон значений
Заголовок раздела «Диапазон значений»Для чисел можно сопоставить целый диапазон через ..=:
fn main() { let score = 85;
match score { 90..=100 => println!("A"), 75..=89 => println!("B"), 60..=74 => println!("C"), _ => println!("F"), }}match как выражение
Заголовок раздела «match как выражение»Как и if, match может давать значение:
fn main() { let day = 6;
let kind = match day { 1 | 2 | 3 | 4 | 5 => "будний день", 6 | 7 => "выходной", _ => "неизвестно", };
println!("{kind}");}Сопоставление текста и символов
Заголовок раздела «Сопоставление текста и символов»match работает не только с числами. Он умеет сравнивать строковые срезы (&str) и символы, а диапазоны символов вроде 'a'..='z' работают так же, как числовые:
fn main() { let command = "stop"; match command { "start" => println!("Запускаю..."), "stop" | "quit" => println!("Останавливаюсь."), _ => println!("Неизвестная команда: {command}"), }
let letter = 'k'; let kind = match letter { 'a' | 'e' | 'i' | 'o' | 'u' => "гласная", 'a'..='z' => "согласная", '0'..='9' => "цифра", _ => "что-то другое", }; println!("'{letter}' — {kind}");
let name = String::from("Ferris"); match name.as_str() { "Ferris" => println!("Привет, краб!"), _ => println!("Привет, {name}!"), }}Вывод:
Останавливаюсь.'k' — согласнаяПривет, краб!Здесь стоит заметить две вещи:
- Порядок важен.
'a'тоже входит в'a'..='z', но ветка с гласными стоит раньше, поэтому до ветки с согласными гласные не доходят. Rust всегда выбирает первую подходящую ветку. - Для
Stringнужен.as_str(). Текст в кавычках, например"Ferris", — это&str. Если сопоставлять с нимStringнапрямую, Rust остановится с ошибкойerror[E0308]: mismatched types ... expected String, found &str(«типы не совпадают: ожидался String, а найден &str»). Вызов.as_str()даёт&str-представление строкиString, с которым образцы уже могут сравнивать. (Чем они отличаются, рассказано в уроке Строки.)
Сравнение учитывает регистр: "Stop" не совпадёт с "stop". Если текст вводит пользователь, часто делают так: match input.to_lowercase().as_str().
match или if/else
Заголовок раздела «match или if/else»Для простых проверок «да/нет» подойдёт и то и другое, но как только вариантов больше двух-трёх, match начинает выигрывать: он читается лучше длинной цепочки else if, а компилятор проверяет, что ты учёл каждый случай. Для одного условия «да или нет» по-прежнему лучше подходит if/else.
Практика
Заголовок раздела «Практика»1. Дни в месяце
Заголовок раздела «1. Дни в месяце»Сохрани номер месяца month (2). С помощью match как выражения вычисли, сколько в нём дней (високосные годы не учитываем), и выведи В месяце 2 дней: 28. Подсказка: в месяцах 1, 3, 5, 7, 8, 10 и 12 по 31 дню — объедини их в одну ветку через |.
Показать решение
fn main() { let month = 2;
let days = match month { 1 | 3 | 5 | 7 | 8 | 10 | 12 => 31, 4 | 6 | 9 | 11 => 30, 2 => 28, _ => 0, // такого месяца нет };
println!("В месяце {month} дней: {days}");}Вывод:
В месяце 2 дней: 282. Коды ответа HTTP
Заголовок раздела «2. Коды ответа HTTP»Веб-серверы отвечают числом. Преврати code в короткое пояснение: 200 — это "OK", 404 — "Не найдено", любой другой код 400–499 — "Ошибка клиента", 500–599 — "Ошибка сервера", а всё остальное — "Неизвестно". Проверь на 503. Подсказка: точную ветку 404 поставь выше диапазона 400..=499, иначе до неё дело никогда не дойдёт.
Показать решение
fn main() { let code = 503;
let meaning = match code { 200 => "OK", 404 => "Не найдено", // должна стоять перед диапазоном 400..=499 400..=499 => "Ошибка клиента", 500..=599 => "Ошибка сервера", _ => "Неизвестно", };
println!("{code}: {meaning}");}Вывод:
503: Ошибка сервераmatchсравнивает значение с образцами и выполняет подходящую ветку.- Нужно обработать все возможные значения — для этого есть заглушка
_. - Значения объединяются через
|, а диапазон задаётся через..=. - Сопоставлять можно и текст
&str, и символыchar; дляStringиспользуй.as_str(). match, как иif, можно использовать как выражение, которое даёт значение.
Проверь себя
Заголовок раздела «Проверь себя»Проверь себя
1. Что делает образец _ в выражении match?
2. Что будет, если match обрабатывает не все возможные значения и в нём нет ветки _?
3. Как сопоставить несколько значений в одной ветке, например 1, 2 или 3?
Результат: 0 / 3
Что почитать
Заголовок раздела «Что почитать»- Конструкция управления потоком match — книга «Язык программирования Rust»: официальная глава, где
matchиспользуется ещё и с перечислениями иOption(перевод на русский). - match — Rust на примерах: компактный пример со значениями,
|, диапазонами и_, который можно запустить (перевод на русский). - Range patterns — The Rust Reference (на английском): какие именно типы и формы диапазонов можно использовать в образце.
- Match expressions — The Rust Reference (на английском): точные правила для веток, условий-охранников (guards) и того, как
matchдаёт значение.