ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 11.01.2024
Просмотров: 1153
Скачиваний: 5
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
переменной x
". Поскольку имя x
является полностью шаблоном, этот шаблон фактически означает "привязать все к переменной x
независимо от значения".
Чтобы более чётко увидеть аспект сопоставления с шаблоном let
, рассмотрим листинг
18-4, в котором используется шаблон с let для деструктурирования кортежа.
". Поскольку имя x
является полностью шаблоном, этот шаблон фактически означает "привязать все к переменной x
независимо от значения".
Чтобы более чётко увидеть аспект сопоставления с шаблоном let
, рассмотрим листинг
18-4, в котором используется шаблон с let для деструктурирования кортежа.
1 ... 45 46 47 48 49 50 51 52 ... 62
Листинг 18-4. Использование шаблона для деструктуризации кортежа и создания трёх переменных
одновременно
Здесь мы сопоставляем кортеж с шаблоном. Rust сравнивает значение
(1, 2, 3)
с шаблоном
(x, y, z)
и видит, что значение соответствует шаблону, поэтому Rust связывает
1
с x
,
2
с y
и
3
с z
. Вы можете думать об этом шаблоне кортежа как о вложении в него трёх отдельных шаблонов переменных.
Если количество элементов в шаблоне не совпадает с количеством элементов в кортеже,
то весь тип не будет совпадать и мы получим ошибку компилятора. Например, в листинге 18-5 показана попытка деструктурировать кортеж с тремя элементами в две переменные, что не будет работать.
Листинг 18-5: Неправильное построение шаблона, переменные не соответствуют количеству элементов в
кортеже
Попытка скомпилировать этот код приводит к ошибке:
Чтобы исправить ошибку, мы могли бы игнорировать одно или несколько значений в кортеже, используя
_
или
, как вы увидите в разделе
“Игнорирование значений в
Шаблоне”
. Если шаблон содержит слишком много переменных в шаблоне, можно let
(x, y, z) = (
1
,
2
,
3
); let
(x, y) = (
1
,
2
,
3
);
$
cargo run
Compiling patterns v0.1.0 (file:///projects/patterns) error[E0308]: mismatched types
-->
src/main.rs:2:9
|
2 | let (x, y) = (1, 2, 3);
| ^^^^^^ --------- this expression has type `({integer}, {integer},
{integer})`
| |
| expected a tuple with 3 elements, found one with 2 elements
|
= note: expected tuple `({integer}, {integer}, {integer})` found tuple `(_, _)`
For more information about this error, try `rustc --explain E0308`. error: could not compile `patterns` due to previous error
решить проблему, сделав типы совпадающими, удалив некоторые переменные таким образом, чтобы число переменных равнялось числу элементов в кортеже.
Параметры функции
Параметры функции также могут быть образцами. Код в листинге 18-6 объявляет функцию с именем foo
, которая принимает один параметр с именем x
типа i32
, к настоящему времени это должно выглядеть знакомым.
Листинг 18-6: Сигнатура функции использует образцы в параметрах
x это часть шаблона! Как и в случае с let
, мы можем сопоставить кортеж в аргументах функции с образцом. Листинг 18-7 разделяет значения в кортеже при его передачи в функцию.
Файл: src/main.rs
Листинг 18-7: Функция с параметрами, которая разрушает кортеж
Этот код печатает текущие координаты: (3, 5)
. Значения
&(3, 5)
соответствуют образцу
&(x, y)
, поэтому x
- это значение
3
, а y
- это значение
5
Добавляя к вышесказанному, мы можем использовать шаблоны в списках параметров замыкания таким же образом, как и в списках параметров функции, потому что, как обсуждалось в главе 13, замыкания похожи на функции.
На данный момент вы видели несколько способов использования шаблонов, но шаблоны работают не одинаково во всех местах, где их можно использовать. В
некоторых местах шаблоны должны быть неопровержимыми; в других обстоятельствах они могут быть опровергнуты. Мы обсудим эти две концепции далее.
fn foo
(x: i32
) {
// code goes here
} fn print_coordinates
(&(x, y): &(
i32
, i32
)) { println!
(
"Current location: ({}, {})"
, x, y);
} fn main
() { let point = (
3
,
5
); print_coordinates(&point);
}
Параметры функции
Параметры функции также могут быть образцами. Код в листинге 18-6 объявляет функцию с именем foo
, которая принимает один параметр с именем x
типа i32
, к настоящему времени это должно выглядеть знакомым.
Листинг 18-6: Сигнатура функции использует образцы в параметрах
x это часть шаблона! Как и в случае с let
, мы можем сопоставить кортеж в аргументах функции с образцом. Листинг 18-7 разделяет значения в кортеже при его передачи в функцию.
Файл: src/main.rs
Листинг 18-7: Функция с параметрами, которая разрушает кортеж
Этот код печатает текущие координаты: (3, 5)
. Значения
&(3, 5)
соответствуют образцу
&(x, y)
, поэтому x
- это значение
3
, а y
- это значение
5
Добавляя к вышесказанному, мы можем использовать шаблоны в списках параметров замыкания таким же образом, как и в списках параметров функции, потому что, как обсуждалось в главе 13, замыкания похожи на функции.
На данный момент вы видели несколько способов использования шаблонов, но шаблоны работают не одинаково во всех местах, где их можно использовать. В
некоторых местах шаблоны должны быть неопровержимыми; в других обстоятельствах они могут быть опровергнуты. Мы обсудим эти две концепции далее.
fn foo
(x: i32
) {
// code goes here
} fn print_coordinates
(&(x, y): &(
i32
, i32
)) { println!
(
"Current location: ({}, {})"
, x, y);
} fn main
() { let point = (
3
,
5
); print_coordinates(&point);
}
Возможность опровержения: может ли шаблон не
совпадать
Шаблоны бывают двух форм: опровержимые и неопровержимые. Шаблоны, которые будут соответствовать любому возможному переданному значению, являются
неопровержимыми (irrefutable). Примером может быть x
в выражении let x = 5;
,
потому что x
соответствует чему-либо и следовательно не может не совпадать.
Шаблоны, которые могут не соответствовать некоторому возможному значению,
являются опровержимыми (refutable). Примером может быть
Some(x)
в выражении if let Some(x) = a_value
, потому что если значение в переменной a_value равно
None
, а не
Some
, то шаблон
Some(x)
не будет совпадать.
Параметры функций, операторы let и for могут принимать только неопровержимые шаблоны, поскольку программа не может сделать ничего значимого, если значения не совпадают. А выражения if let и while let принимают опровержимые и неопровержимые шаблоны, но компилятор предостерегает от неопровержимых шаблонов, поскольку по определению они предназначены для обработки возможного сбоя: функциональность условного выражения заключается в его способности выполнять разный код в зависимости от успеха или неудачи.
В общем случае, вам не нужно беспокоиться о разнице между опровержимыми
(refutable) и неопровержимыми (irrefutable) образцами; тем не менее, вам необходимо ознакомиться с концепцией возможности опровержения, чтобы вы могли отреагировать на неё, увидев в сообщении об ошибке. В таких случаях вам потребуется изменить либо шаблон, либо конструкцию с которой вы используете шаблон в зависимости от предполагаемого поведения кода.
Давайте посмотрим на пример того, что происходит, когда мы пытаемся использовать опровержимый (refutable) шаблон, где Rust требует неопровержимый шаблон и наоборот. В листинге 18-8 показан оператор let
, но для образца мы указали
Some(x)
являющийся шаблоном, который можно опровергнуть. Как и следовало ожидать, этот код не будет компилироваться.
Листинг 18-8: Попытка использовать опровержимый шаблон вместе с
let
Если some_option_value было бы значением
None
, то оно не соответствовало бы шаблону
Some(x)
, что означает, что шаблон является опровержимым. Тем не менее,
оператор let может принимать только неопровержимый шаблон, потому что нет корректного кода, который может что-то сделать со значением
None
. Во время компиляции Rust будет жаловаться на то, что мы пытались использовать опровержимый шаблон для которого требуется неопровержимый шаблон:
let
Some
(x) = some_option_value;
Поскольку мы не покрыли (и не могли покрыть!) каждое допустимое значение с помощью образца
Some(x)
, то Rust выдаёт ошибку компиляции.
Чтобы исправить проблему наличия опровержимого шаблона, там где нужен неопровержимый шаблон, можно изменить код использующий шаблон: вместо использования let
, можно использовать if let
. Затем, если шаблон не совпадает,
выполнение кода внутри фигурных скобок будет пропущено, что даст возможность продолжить корректное выполнение. В листинге 18-9 показано, как исправить код из листинга 18-8.
Листинг 18-9. Использование
if let
и блока с опровергнутыми шаблонами вместо
let
Код сделан! Этот код совершенно корректный, хотя это означает, что мы не можем использовать неопровержимый образец без получения ошибки. Если мы используем шаблон if let
, который всегда будет совпадать, то для примера x
показанного в листинге 18-10, компилятор выдаст предупреждение.
Листинг 18-10. Попытка использовать неопровержимый шаблон с
if let
Rust жалуется, что не имеет смысла использовать, if let с неопровержимым образцом:
$
cargo run
Compiling patterns v0.1.0 (file:///projects/patterns) error[E0005]: refutable pattern in local binding: `None` not covered
-->
src/main.rs:3:9
|
3 | let Some(x) = some_option_value;
| ^^^^^^^ pattern `None` not covered
|
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an
`enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02- refutability.html note: `Option
= note: the matched value is of type `Option
|
3 | let x = if let Some(x) = some_option_value { x } else { todo!() };
| ++++++++++ ++++++++++++++++++++++
For more information about this error, try `rustc --explain E0005`. error: could not compile `patterns` due to previous error if let
Some
(x) = some_option_value { println!
(
"{}"
, x);
} if let x =
5
{ println!
(
"{}"
, x);
};
По этой причине совпадающие рукава должны использовать опровержимые образцы, за исключением последнего, который должен сопоставлять любые оставшиеся значения с неопровержимым образцом. Rust позволяет нам использовать неопровержимый шаблон в match только с одним рукавом, но этот синтаксис не особенно полезен и может быть заменён более простым оператором let
Теперь, когда вы знаете, где использовать шаблоны и разницу между опровержимыми и неопровержимыми шаблонами, давайте рассмотрим весь синтаксис, который мы можем использовать для создания шаблонов.
$
cargo run
Compiling patterns v0.1.0 (file:///projects/patterns) warning: irrefutable `if let` pattern
-->
src/main.rs:2:8
|
2 | if let x = 5 {
| ^^^^^^^^^
|
= note: `#[warn(irrefutable_let_patterns)]` on by default
= note: this pattern will always match, so the `if let` is useless
= help: consider replacing the `if let` with a `let` warning: `patterns` (bin "patterns") generated 1 warning
Finished dev [unoptimized + debuginfo] target(s) in 0.39s
Running `target/debug/patterns`
5
Синтаксис шаблонов
В этом разделе мы рассмотрим все допустимые выражения в шаблонах и обсудим, зачем и когда они могут пригодиться.
Сопоставление с литералом
Как мы уже видели в главе 6, можно сопоставлять с литералами напрямую. В следующем коде есть несколько примеров:
Этот код печатает one
, потому что значение в x
равно 1. Данный синтаксис полезен,
когда вы хотите, чтобы ваш код предпринял действие, если он получает конкретное значение.
Сопоставление именованных переменных
Именованные переменные - это неопровержимые (irrefutable) шаблоны, которые соответствуют любому значению и мы использовали их много раз в книге. Однако при использовании именованных переменных в выражениях match возникает сложность.
Поскольку match начинает новую область видимости, то переменные, объявленные как часть шаблона внутри выражения match
, будут затенять переменные с тем же именем вне конструкции match как и в случае со всеми переменными. В листинге 18-11 мы объявляем переменную с именем x
со значением
Some(5)
и переменную y
со значением
10
. Затем мы создаём выражение match для значения x
. Посмотрите на шаблоны в ветках, println!
в конце и попытайтесь выяснить, какой код будет напечатан прежде чем запускать его или читать дальше.
Файл: src/main.rs let x =
1
; match x {
1
=> println!
(
"one"
),
2
=> println!
(
"two"
),
3
=> println!
(
"three"
),
_ => println!
(
"anything"
),
}
Листинг 18-11: Выражение
match
с веткой, которая добавляет затенённую переменную
y
Давайте рассмотрим, что происходит, когда выполняется выражение match
. Шаблон в первой ветке не соответствует определённому значению x
, поэтому выполнение продолжается.
Шаблон во второй ветке вводит новую переменную с именем y
, которая будет соответствовать любому значению в
Some
. Поскольку мы находимся в новой области видимости внутри выражения match
, это новая переменная y
, а не y
которую мы объявили в начале со значением 10. Эта новая привязка y
будет соответствовать любому значению из
Some
, которое находится в x
. Следовательно, эта новая y
связывается с внутренним значением
Some из переменной x
. Этим значением является
5
, поэтому выражение для этой ветки выполняется и печатает
Matched, y = 5
Если бы x
было значением
None вместо
Some(5)
, то шаблоны в первых двух ветках не совпали бы, поэтому значение соответствовало бы подчёркиванию. Мы не ввели переменную x
в шаблоне ветки со знаком подчёркивания, поэтому x
в выражении все ещё является внешней переменной x
, которая не была затенена. В этом гипотетическом случае совпадение match выведет
Default case, x = None
Когда выражение match завершается, заканчивается его область видимости как и область действия внутренней переменной y
. Последний println!
печатает at the end: x = Some(5), y = 10
Чтобы создать выражение match
, которое сравнивает значения внешних x
и y
, вместо введения затенённой переменной нужно использовать условие в сопоставлении образца. Мы поговорим про условие в сопоставлении шаблона позже в разделе
“Дополнительные условия в сопоставлении образца”
Группа шаблонов
В выражениях match можно сравнивать сразу с несколькими шаблонами, используя синтаксис
|
, который является оператором паттерна or. Например, в следующем примере мы сопоставляем значение x
с ветвями match, первая из которых содержит let x =
Some
(
5
); let y =
10
; match x {
Some
(
50
) => println!
(
"Got 50"
),
Some
(y) => println!
(
"Matched, y = {y}"
),
_ => println!
(
"Default case, x = {:?}"
, x),
} println!
(
"at the end: x = {:?}, y = {y}"
, x);