Места использования шаблонов

Шаблоны можно использовать в различных частях синтаксических конструкций. Вы уже использовали их даже не осознавая этого. В этой секции мы расскажем о местах, где можно использовать шаблоны. Patterns pop up in a number of places in Rust. You’ve been using them a lot without realizing it! This section is a reference to all the places where patterns are valid.

Рукава match

Как мы уже обсуждали в главе 6, основным местом, где шаблоны используются - это рукава выражений match. Формально, выражения match определятся как объединение ключевого слова match, значения которое будет сравниваться и одно или несколько рукавов, которые составляют шаблон и выражение, которое будет выполнено, если значение будет соответствовать шаблону:

match VALUE {
    PATTERN => EXPRESSION,
    PATTERN => EXPRESSION,
    PATTERN => EXPRESSION,
}

Исчерпываемость и шаблон по умолчанию _

Выражения match должны охватывать все возможные варианты значения.

Есть специальный символ _. Его надо располагать последним в списке. Этот шаблон выбирает все остальные варианты. Этот шаблон удобно использовать чтобы игнорировать все остальные варианты.

Выражения if let

Мы уже обсуждали выражение if let в главер 6. Мы использовали компактную замену match для поиска по одному варианту. if let также может иметь else для выполнения блока кода, если условие поиске не выполнено.

Код 18-1 показывает как можно совместно использовать if let, else if и else if let. Следующий код демонстрирует серию проверок цвета фона. Для этой цели мы создали имитацию ввода данных пользователем. Если пользователь ввел любимый цвет - мы сообщим этот цвет. Если сегодня вторник - фон основания будет зелёным. Если пользователь указал свой возраст и мы смогли прочитать число, цвет будет зависеть от значения числа. Если ни одно из условий не будет выполнено цвет фона будет синим:

Filename: src/main.rs

fn main() {
    let favorite_color: Option<&str> = None;
    let is_tuesday = false;
    let age: Result<u8, _> = "34".parse();

    if let Some(color) = favorite_color {
        println!("Using your favorite color, {}, as the background", color);
    } else if is_tuesday {
        println!("Tuesday is green day!");
    } else if let Ok(age) = age {
        if age > 30 {
            println!("Using purple as the background color");
        } else {
            println!("Using orange as the background color");
        }
    } else {
        println!("Using blue as the background color");
    }
}

код 18-1: смешенное использование if let, else if, else if let и else

Как вы видите схема проверки довольно-таки сложная для восприятия и вполне возможно не покрывает всех случаев. В отличии от таких конструкций match и компилятор помогают выявить возможные ошибки. Так что мы рекомендуем использовать этот оператор при решении сложных задач поиска данных.

while let

Конструкция while let похожа на if let. Она позволяет выполнять цикл пока выражение внутри while let соответствует шаблону. Пример 18-2 использует вектор, в качестве стека и печатает значения в вектор в противоположном направлении:


# #![allow(unused_variables)]
#fn main() {
let mut stack = Vec::new();

stack.push(1);
stack.push(2);
stack.push(3);

while let Some(top) = stack.pop() {
    println!("{}", top);
}
#}

код 18-2: использование while let для печати значений в цикле пока метод stack.pop() возвращает Some

Этот пример построчно выведет содержание вектора: 3, 2 и затем 1. Метод pop возвращает последний элемент в списке Some(value) и удаляет его из вектора. Если вектор пустой, он возвращает None. Цикл while продолжит выполнять код внутри блока пока метод pop возвращает Some. Как только он вернёт None, цикл while прекращается. Мы можем использовать while let для выборки каждого элемента из стека.

Цикл for

Работа цикла for уже была освещена в главе 3. Это наиболее часто используемый цикл в Rust. В примере 18-3 продемонстрировано, как мы можем использовать шаблон для получения кортежа - (значение, его индекс):


# #![allow(unused_variables)]
#fn main() {
let v = vec![1, 2, 3];

for (index, value) in v.iter().enumerate() {
    println!("{} is at index {}", value, index);
}
#}

код 18-3: использование цикла for для получения кортежа:

Будет напечатано:

1 is at index 0
2 is at index 1
3 is at index 2

Первый вызов enumerate создаёт кортеж (0, 1). В нём содержится индекс и значение (index, value), где index равен 0, а value будет равно 1.

Оператор let

match и if let я являются конечным списком, где используется шаблоны. Рассмотрим обычное объявление переменной let:


# #![allow(unused_variables)]
#fn main() {
let x = 5;
#}

Мы проделывали это уже сотни раз, реализуя примеры учебника. Возможно, вы не осознавали, но вы использовали шаблоны всё это время. Формально оператор выглядит так:

let PATTERN = EXPRESSION;

Мы видим операторы, такие как let x = 5;, где имя переменной является PATTERN.

С помощью let мы сравниваем выражение с шаблоном и присваиваем ему значение. В примере let x = 5; написано, что необходимо связать всё с переменой x.

Может быть и такой вариант::


# #![allow(unused_variables)]
#fn main() {
let (x, y, z) = (1, 2, 3);
#}

код 18-4: использование шаблона для создания 3-х переменных

Мы также видели другой пример разбора кортежа на переменные (в главе 16), где функция mpsc::channel() возвращает кортеж (tx, rx).

Параметры функции

Также как и let, параметры функции также могут быть шаблоном. Код 18-5 демонстрирует объявление функции foo, у которой есть параметр с именем x, типа i32:


# #![allow(unused_variables)]
#fn main() {
fn foo(x: i32) {
    // code goes here
}
#}

код 18-5: объявление функции

Демонстрация того, как фунция может иметь параметр тип кортеж:

Filename: src/main.rs

fn print_coordinates(&(x, y): &(i32, i32)) {
    println!("Current location: ({}, {})", x, y);
}

fn main() {
    let point = (3, 5);
    print_coordinates(&point);
}

код 18-6: объявление функции с кортежем в виде параметра

Будет напечатано Current location: (3, 5). Когда мы отправим значение &(3, 5) в функцию print_coordinates, значения будет сопоставлено с шаблоном &(x, y). x получит значение 3, а y получит значение 5.

Т.к. замыкания похожи на функции (мы их обсуждали в главе 13), мы также можем использовать шаблоны в списках параметров замыканий.

То что роднит шаблоны в циклах for,let и параметрах функции - это то что они должны быть однозначными. Подробнее об этом поговорим в следующей секции.