Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Сопоставление

Ключевое слово 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 продолжит проверять последующие варианты.

This slide should take about 10 minutes.

Ключевые моменты:

  • Можно обратить внимание на то, как в шаблонах используются некоторые специальные символы.

    • | — как 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). Это используется редко, но может быть полезно в более сложных шаблонах.