Паттерны для захвата всех случаев и символ подстановки _
Используя перечисления, мы также можем выполнять специальные действия для нескольких конкретных значений, а для всех остальных значений выполнять одно действие по умолчанию. Представьте, что мы реализуем игру, в которой, если вы выпадает 3 при броске кубика, ваш игрок не двигается, а получает новую роскошную шляпу. Если вы выпадает 7, ваш игрок теряет роскошную шляпу. Для всех остальных значений ваш игрок двигается на такое количество клеток по игровому полю. Вот match
, который реализует эту логику, с результатом броска кубика жестко закодированным вместо случайного значения, и всей другой логикой, представленной функциями без тела, потому что фактическое их реализация находится вне области применения данного примера:
let dice_roll = 9;
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
1 other => move_player(other),
}
fn add_fancy_hat() {}
fn remove_fancy_hat() {}
fn move_player(num_spaces: u8) {}
Для первых двух ветвей шаблоны - это литеральные значения 3
и 7
. Для последней ветви, которая охватывает все остальные возможные значения, шаблон - это переменная, которую мы выбрали назвать other
[1]. Код, который выполняется для ветви other
, использует переменную, передав ее в функцию move_player
.
Этот код компилируется, хотя мы не перечислили все возможные значения, которые может иметь u8
, потому что последний шаблон будет соответствовать всем значениям, не перечисленным специально. Этот шаблон для захвата всех случаев соответствует требованию, что match
должен быть исчерпывающим. Обратите внимание, что мы должны поместить ветвь для захвата всех случаев в конец, потому что шаблоны оцениваются по порядку. Если мы поместим ветвь для захвата всех случаев раньше, другие ветви никогда не будут выполняться, поэтому Rust предупредит нас, если мы добавим ветви после ветви для захвата всех случаев!
В Rust также есть шаблон, который мы можем использовать, когда хотим захватить все случаи, но не хотим использовать значение в шаблоне для захвата всех случаев: _
- это специальный шаблон, который соответствует любому значению и не связывается с этим значением. Это сообщает Rust, что мы не собираемся использовать значение, поэтому Rust не будет предупреждать нас о неиспользуемой переменной.
Давайте изменим правила игры: теперь, если вы выпадает что-то, кроме 3 или 7, вам нужно снова бросить кубик. Мы больше не нуждаемся в значении для захвата всех случаев, поэтому мы можем изменить наш код и использовать _
вместо переменной с именем other
:
let dice_roll = 9;
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
_ => reroll(),
}
fn add_fancy_hat() {}
fn remove_fancy_hat() {}
fn reroll() {}
Этот пример также соответствует требованию исчерпываемости, потому что мы явно игнорируем все остальные значения в последней ветви; мы ничего не забыли.
Наконец, мы снова изменим правила игры так, чтобы ничего другого не происходило в ходе вашего хода, если вы выпадает что-то, кроме 3 или 7. Мы можем выразить это, используя единичное значение (пустой тип кортежа, который мы упоминали в разделе "Тип кортежа") в качестве кода, который идет с ветвью _
:
let dice_roll = 9;
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
_ => (),
}
fn add_fancy_hat() {}
fn remove_fancy_hat() {}
Здесь мы явно сообщаем Rust, что мы не собираемся использовать любые другие значения, которые не соответствуют шаблону в более ранней ветви, и мы не хотим выполнять никакой код в этом случае.
Есть еще много о шаблонах и сопоставлении, которое мы рассмотрим в главе 18. На данный момент мы перейдем к синтаксису if let
, который может быть полезен в ситуациях, когда выражение match
выглядит несколько избыточным.