Возможность опровержения: может ли шаблон не совпадать
Шаблоны бывают двух форм: опровержимые и неопровержимые. Шаблоны, которые будут соответствовать любому возможному переданному значению, являются неопровержимыми (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) шаблонами; тем не менее, вам необходимо ознакомиться с концепцией возможности опровержения, чтобы вы могли отреагировать на неё, увидев в сообщении об ошибке. В таких случаях вам потребуется изменить либо шаблон, либо конструкцию, с которой вы используете шаблон, в зависимости от предполагаемого поведения кода.
Давайте посмотрим на пример того, что происходит, когда мы пытаемся использовать опровержимый шаблон, где Rust требует неопровержимый шаблон, и наоборот. В листинге 18-8 показана инструкция let
, но для образца мы указали Some(x)
, являющийся шаблоном, который можно опровергнуть. Как и следовало ожидать, этот код не будет компилироваться.
{{#rustdoc_include ../listings/ch18-patterns-and-matching/listing-18-08/src/main.rs:here}}
Если some_option_value
было бы значением None
, то оно не соответствовало бы шаблону Some(x)
, что означает, что шаблон является опровержимым. Тем не менее, инструкция let
может принимать только неопровержимый шаблон, потому что нет корректного кода, который может что-то сделать со значением None
. Во время компиляции Rust будет жаловаться на то, что мы пытались использовать опровержимый шаблон, для которого требуется неопровержимый шаблон:
{{#include ../listings/ch18-patterns-and-matching/listing-18-08/output.txt}}
Поскольку мы не покрыли (и не могли покрыть!) каждое допустимое значение с помощью образца Some(x)
, то Rust выдаёт ошибку компиляции.
Чтобы исправить проблему наличия опровержимого шаблона, там, где нужен неопровержимый шаблон, можно изменить код, использующий шаблон: вместо использования let
, можно использовать if let
. Затем, если шаблон не совпадает, выполнение кода внутри фигурных скобок будет пропущено, что даст возможность продолжить корректное выполнение. В листинге 18-9 показано, как исправить код из листинга 18-8.
#![allow(unused)] fn main() { {{#rustdoc_include ../listings/ch18-patterns-and-matching/listing-18-09/src/main.rs:here}} }
Код исправлен! Этот код совершенно корректный, хотя это означает, что мы не можем использовать неопровержимый образец без получения ошибки. Если мы используем шаблон if let
, который всегда будет совпадать, то для примера x
, показанного в листинге 18-10, компилятор выдаст предупреждение.
#![allow(unused)] fn main() { {{#rustdoc_include ../listings/ch18-patterns-and-matching/listing-18-10/src/main.rs:here}} }
Rust жалуется, что не имеет смысла использовать if let
с неопровержимым образцом:
{{#include ../listings/ch18-patterns-and-matching/listing-18-10/output.txt}}
По этой причине совпадающие ветки выражений должны использовать опровержимые шаблоны, за исключением последнего, который должен сопоставлять любые оставшиеся значения с неопровержимым шаблоном. Rust позволяет нам использовать неопровержимый шаблон в match
только с одной веткой, но этот синтаксис не особенно полезен и может быть заменён более простой инструкцией let
.
Теперь, когда вы знаете, где использовать шаблоны и разницу между опровержимыми и неопровержимыми шаблонами, давайте рассмотрим весь синтаксис, который мы можем использовать для создания шаблонов.