Дополнительные условия с использованием защитных выражений в match
Защитное выражение в match
(match guard) — это дополнительное условие if
, указанное после шаблона в ветке match
, которое также должно быть выполнено, чтобы эта ветка была выбрана. Защитные выражения в match
полезны для выражения более сложных идей, чем позволяет только шаблон.
Условие может использовать переменные, созданные в шаблоне. Листинг 18 - 26 показывает выражение match
, где первая ветка имеет шаблон Some(x)
и также содержит защитное выражение if x % 2 == 0
(которое будет true
, если число четное).
Имя файла: src/main.rs
let num = Some(4);
match num {
Some(x) if x % 2 == 0 => println!("The number {x} is even"),
Some(x) => println!("The number {x} is odd"),
None => (),
}
Листинг 18 - 26: Добавление защитного выражения в шаблон
В этом примере будет напечатано The number 4 is even
. Когда num
сравнивается с шаблоном в первой ветке, они совпадают, так как Some(4)
соответствует Some(x)
. Затем защитное выражение проверяет, равно ли остаток от деления x
на 2 нулю, и так как это так, выбирается первая ветка.
Если бы num
было равно Some(5)
, защитное выражение в первой ветке было бы false
, так как остаток от деления 5 на 2 равен 1, который не равен 0. Rust перейдет к второй ветке, которая совпадет, так как во второй ветке нет защитного выражения и, следовательно, она совпадает с любым вариантом Some
.
Нет способа выразить условие if x % 2 == 0
внутри шаблона, поэтому защитное выражение в match
позволяет нам выразить такую логику. Недостаток этого дополнительного выразительного способа заключается в том, что компилятор не пытается проверить исчерпываемость, когда используются защитные выражения в match
.
В листинге 18 - 11 мы упоминали, что можно использовать защитные выражения в match
для решения проблемы переопределения переменных (pattern - shadowing). Напомним, что мы создали новую переменную внутри шаблона в выражении match
вместо использования переменной вне match
. Эта новая переменная означает, что мы не можем проверить значение внешней переменной. Листинг 18 - 27 показывает, как можно использовать защитное выражение в match
для решения этой проблемы.
Имя файла: src/main.rs
fn main() {
let x = Some(5);
let y = 10;
match x {
Some(50) => println!("Got 50"),
Some(n) if n == y => println!("Matched, n = {n}"),
_ => println!("Default case, x = {:?}", x),
}
println!("at the end: x = {:?}, y = {y}", x);
}
Листинг 18 - 27: Использование защитного выражения в match
для проверки равенства с внешней переменной
Этот код теперь напечатает Default case, x = Some(5)
. Шаблон во второй ветке match
не вводит новую переменную y
, которая переопределила бы внешнюю y
, что означает, что мы можем использовать внешнюю y
в защитном выражении. Вместо того чтобы указать шаблон как Some(y)
, который переопределил бы внешнюю y
, мы указываем Some(n)
. Это создает новую переменную n
, которая не переопределяет ничего, так как вне match
нет переменной n
.
Защитное выражение if n == y
не является шаблоном и, следовательно, не вводит новые переменные. Эта y
является внешней y
, а не новой переопределенной y
, и мы можем искать значение, равное внешней y
, сравнивая n
с y
.
Вы также можете использовать оператор или |
в защитном выражении в match
, чтобы указать несколько шаблонов; условие защитного выражения будет применяться ко всем шаблонам. Листинг 18 - 28 показывает приоритет, когда комбинируется шаблон, использующий |
, с защитным выражением в match
. Важная часть этого примера заключается в том, что защитное выражение if y
применяется к 4
, 5
и 6
, даже если может показаться, что if y
применяется только к 6
.
Имя файла: src/main.rs
let x = 4;
let y = false;
match x {
4 | 5 | 6 if y => println!("yes"),
_ => println!("no"),
}
Листинг 18 - 28: Комбинирование нескольких шаблонов с защитным выражением в match
Условие в match
гласит, что ветка совпадает только если значение x
равно 4
, 5
или 6
и если y
равно true
. Когда этот код выполняется, шаблон первой ветки совпадает, так как x
равно 4
, но защитное выражение if y
равно false
, поэтому первая ветка не выбирается. Код переходит ко второй ветке, которая совпадает, и программа печатает no
. Причина в том, что условие if
применяется ко всему шаблону 4 | 5 | 6
, а не только к последнему значению 6
. Другими словами, приоритет защитного выражения в match
по отношению к шаблону выглядит так:
(4 | 5 | 6) if y =>...
а не так:
4 | 5 | (6 if y) =>...
После выполнения кода поведение приоритета становится очевидным: если бы защитное выражение применялось только к последнему значению в списке значений, указанных с использованием оператора |
, ветка бы совпала и программа напечатала бы yes
.