Сопоставление
Ключевое слово match позволяет сопоставить значение с одним или несколькими шаблонами. Шаблоны могут быть простыми значениями, как switch в C и C++, но также могут выражать более сложные условия:
// Copyright 2024 Google LLC
// SPDX-License-Identifier: Apache-2.0
#[rustfmt::skip]
fn main() {
let input = 'x';
match input {
'q' => println!("Quitting"),
'a' | 's' | 'w' | 'd' => println!("Moving around"),
'0'..='9' => println!("Number input"),
key if key.is_lowercase() => println!("Lowercase: {key}"),
_ => println!("Something else"),
}
}
Переменная в шаблоне (здесь key) создаёт привязку, которую можно использовать внутри ветки match. Подробнее об этом мы узнаем на следующем слайде.
Охранник совпадения (match guard) делает так, что ветка срабатывает, только если условие истинно. Если условие ложно, match продолжит проверять последующие варианты.
Ключевые моменты:
-
Можно обратить внимание на то, как в шаблонах используются некоторые специальные символы.
|— какor..совпадает с любым количеством элементов1..=5— включающий диапазон_— шаблон-подстановка
-
Охранники совпадения как отдельная возможность синтаксиса важны и нужны, когда мы хотим лаконично выразить более сложные идеи, чем позволяют одни только шаблоны.
-
Охранники совпадения отличаются от выражений
ifпосле=>. Выражениеifвычисляется после того, как выбрана веткаmatch. Если условиеifвнутри этого блока ложно, другие ветки исходного выраженияmatchне рассматриваются. В следующем примере шаблон-подстановка_ =>даже не проверяется.
// Copyright 2024 Google LLC
// SPDX-License-Identifier: Apache-2.0
#[rustfmt::skip]
fn main() {
let input = 'a';
match input {
key if key.is_uppercase() => println!("Uppercase"),
key => if input == 'q' { println!("Quitting") },
_ => println!("Bug: this is never printed"),
}
}
-
Условие, заданное в охраннике, применяется к каждому выражению в шаблоне с
|. -
Обратите внимание, что в условии ветки
matchнельзя использовать существующую переменную: она будет истолкована как шаблон с именем переменной, который создаёт новую переменную и затеняет существующую. Например:#![allow(unused)] fn main() { // Copyright 2024 Google LLC // SPDX-License-Identifier: Apache-2.0 let expected = 5; match 123 { expected => println!("Expected value is 5, actual is {expected}"), _ => println!("Value was something else"), } }Здесь мы пытаемся сопоставить число 123, и хотим, чтобы первый случай проверял, равно ли значение 5. Наивно ожидать, что первый случай не совпадёт, ведь значение не равно 5. Но на самом деле это истолковывается как шаблон-переменная, который всегда совпадает, поэтому первая ветка будет выбрана всегда. Если вместо этого использовать константу, всё заработает как ожидается.
Для дальнейшего изучения
-
Ещё один элемент синтаксиса шаблонов, который можно показать студентам, —
@, который связывает часть шаблона с переменной. Например:#![allow(unused)] fn main() { // Copyright 2024 Google LLC // SPDX-License-Identifier: Apache-2.0 let opt = Some(123); match opt { outer @ Some(inner) => { println!("outer: {outer:?}, inner: {inner}"); } None => {} } }В этом примере
innerимеет значение 123, которое извлечено изOptionчерез деструктуризацию, аouterзахватывает всё выражениеSome(inner), поэтому содержит полныйOption::Some(123). Это используется редко, но может быть полезно в более сложных шаблонах.