Главная страница
Навигация по странице:

  • Листинг 18-5: Неправильное построение шаблона, переменные не соответствуют количеству элементов в кортеже

  • Листинг 18-6: Сигнатура функции использует образцы в параметрах

  • Листинг 18-7: Функция с параметрами, которая разрушает кортеж

  • Возможность опровержения: может ли шаблон не совпадать

  • Листинг 18-8: Попытка использовать опровержимый шаблон вместе с

  • Листинг 18-9. Использование if let и блока с опровергнутыми шаблонами вместо

  • Листинг 18-10. Попытка использовать неопровержимый шаблон с

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

  • Сопоставление именованных переменных

  • Листинг 18-11: Выражение match с веткой, которая добавляет затенённую переменную

  • Сопоставление диапазонов с помощью

  • Деструктуризация для получения значений

  • Деструктуризация структуры

  • Листинг 18-12: Разбиение полей структуры в отдельные переменные

  • Листинг 18-13: Деструктуризация полей структуры с использованием сокращённой записи

  • Листинг 18-14: Деструктуризация и сопоставление с литералами в одном шаблоне

  • Язык программирования Rust


    Скачать 7.02 Mb.
    НазваниеЯзык программирования Rust
    Дата12.04.2023
    Размер7.02 Mb.
    Формат файлаpdf
    Имя файлаThe Rust Programming Language_ru.pdf
    ТипУчебник
    #1056301
    страница50 из 62
    1   ...   46   47   48   49   50   51   52   53   ...   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);
    }

    Возможность опровержения: может ли шаблон не
    совпадать
    Шаблоны бывают двух форм: опровержимые и неопровержимые. Шаблоны, которые будут соответствовать любому возможному переданному значению, являются
    неопровержимыми (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` defined here
    = note: the matched value is of type `Option` help: you might want to use `if let` to ignore the variant that isn't matched
    |
    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);
    оператор or, так что если значение x
    совпадёт с любым из значений в этой ветви, то будет выполнен её код:
    Будет напечатано one or two
    Сопоставление диапазонов с помощью ..=
    Синтаксис
    ..=
    позволяет нам выполнять сравнение с диапазоном значений. В
    следующем коде, когда в шаблоне найдётся совпадение с любым из значений заданного диапазона, будет выполнена эта ветка:
    Если x
    равен 1, 2, 3, 4 или 5, то совпадение будет достигнуто в первой ветке. Этот синтаксис более удобен при указании нескольких значений для сравнения, чем использование оператора
    |
    для определения этой же идеи; если бы мы решили использовать
    |
    , нам пришлось бы написать
    1 | 2 | 3 | 4 | 5
    . Указание диапазона намного короче, особенно если мы хотим подобрать, скажем, любое число от 1 до 1 000!
    Компилятор проверяет, что диапазон не является пустым во время компиляции, и поскольку единственными типами, для которых Rust может определить, пуст диапазон или нет, являются char и числовые значения, диапазоны допускаются только с числовыми или char значениями.
    Вот пример использования диапазонов значений char
    :
    let x =
    1
    ; match x {
    1
    |
    2
    => println!
    (
    "one or two"
    ),
    3
    => println!
    (
    "three"
    ),
    _ => println!
    (
    "anything"
    ),
    } let x =
    5
    ; match x {
    1
    ..=
    5
    => println!
    (
    "one through five"
    ),
    _ => println!
    (
    "something else"
    ),
    } let x =
    'c'
    ; match x {
    'a'
    ..=
    'j'
    => println!
    (
    "early ASCII letter"
    ),
    'k'
    ..=
    'z'
    => println!
    (
    "late ASCII letter"
    ),
    _ => println!
    (
    "something else"
    ),
    }

    Rust может сообщить, что 'c'
    находится в диапазоне первого шаблона и напечатать начальную букву ASCII
    Деструктуризация для получения значений
    Мы также можем использовать шаблоны для деструктуризации структур, перечислений и кортежей, чтобы использовать разные части этих значений. Давайте пройдёмся по каждому варианту.
    Деструктуризация структуры
    В листинге 18-12 показана структура
    Point с двумя полями x
    и y
    , которые мы можем разделить, используя шаблон с выражением let
    Файл: src/main.rs
    Листинг 18-12: Разбиение полей структуры в отдельные переменные
    Этот код создаёт переменные a
    и b
    , которые сопоставляются значениям полей x
    и y
    структуры p
    . Этот пример показывает, что имена переменных в шаблоне не обязательно должны совпадать с именами полей структуры. Однако обычно имена переменных сопоставляются с именами полей, чтобы было легче запомнить, какие переменные взяты из каких полей. Из-за этого, а также из-за того, что строчка let Point
    { x: x, y: y } = p;
    содержит много дублирования, в Rust ввели специальное сокращение для шаблонов, соответствующих полям структуры: вам нужно только указать имя поля структуры, и тогда переменные, созданные из шаблона, будут иметь те же имена. Код в листинге 18-13 аналогичен коду в Листинге 18-12, но в шаблоне let создаются переменные x
    и y
    , вместо a
    и b
    Файл: src/main.rs struct
    Point
    { x: i32
    , y: i32
    ,
    } fn main
    () { let p = Point { x:
    0
    , y:
    7
    }; let
    Point { x: a, y: b } = p; assert_eq!
    (
    0
    , a); assert_eq!
    (
    7
    , b);
    }

    Листинг 18-13: Деструктуризация полей структуры с использованием сокращённой записи
    Этот код создаёт переменные x
    и y
    , которые соответствуют полям x
    и y
    из переменной p
    . В результате переменные x
    и y
    содержат значения из структуры p
    Вместо создания переменных для всех полей мы также можем деструктурировать с помощью литеральных значений являющихся частью структуры. Это позволяет проверить некоторые поля на определённые значения при создании переменных для деструктуризации других полей.
    В листинге 18-14 показано выражение match
    , которое разделяет значения
    Point на три случая: точки, которые лежат непосредственно на оси x
    (что верно, когда y = 0
    ), на оси y
    (
    x = 0
    ) или ни то, ни другое.
    Файл: src/main.rs
    Листинг 18-14: Деструктуризация и сопоставление с литералами в одном шаблоне
    Первая ветка будет соответствовать любой точке, которая лежит на оси x
    , указанием того что поле y
    совпадает, если его значение соответствует литералу равному
    0
    Шаблон все ещё создаёт переменную x
    , которую мы можем использовать в коде для этой ветки.
    Точно так же вторая ветка соответствует любой точке на оси y
    , указанием того что поле x
    совпадает, если его значение равно
    0
    и создаёт переменную y
    для значения поля y
    Третья ветка не указывает никаких литералов, поэтому он соответствует любой другой точке
    Point и создаёт переменные для обоих полей x
    и y
    struct
    Point
    { x: i32
    , y: i32
    ,
    } fn main
    () { let p = Point { x:
    0
    , y:
    7
    }; let
    Point { x, y } = p; assert_eq!
    (
    0
    , x); assert_eq!
    (
    7
    , y);
    } fn main
    () { let p = Point { x:
    0
    , y:
    7
    }; match p {
    Point { x, y:
    0
    } => println!
    (
    "On the x axis at {}"
    , x),
    Point { x:
    0
    , y } => println!
    (
    "On the y axis at {}"
    , y),
    Point { x, y } => println!
    (
    "On neither axis: ({}, {})"
    , x, y),
    }
    }

    В этом примере значение p
    совпадает по второй ветке, так как x
    содержит значение 0,
    поэтому этот код будет печатать
    On the y axis at 7
    Помните, что выражение match перестаёт проверять следующие ветви, как только оно находит первый совпадающий шаблон, поэтому, даже если
    Point { x: 0, y: 0}
    находится на оси x
    и оси y
    , этот код будет печатать только
    On the x axis at 0
    1   ...   46   47   48   49   50   51   52   53   ...   62


    написать администратору сайта