ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 11.01.2024
Просмотров: 1150
Скачиваний: 5
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
оператор 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"
),
}
совпадёт с любым из значений в этой ветви, то будет выполнен её код:
Будет напечатано 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
Деструктуризация перечислений
Мы уже деструктурировали перечисления в книге (см., например, листинг 6-5 главы 6), но не обсуждали явно, что шаблон для деструктуризации перечисления должен соответствовать способу объявления данных, хранящихся в перечислении. Например, в листинге 18-15 мы используем перечисление
Message из листинга 6-2 и пишем match с
шаблонами, которые будут деструктурировать каждое внутреннее значение.
Файл: src/main.rs
Листинг 18-15: Деструктуризация вариантов перечисления, содержащих разные виды значений
Этот код напечатает
Change the color to red 0, green 160, and blue 255
. Попробуйте изменить значение переменной msg
, чтобы увидеть выполнение кода в других ветках.
enum
Message
{
Quit,
Move { x: i32
, y: i32
},
Write(
String
),
ChangeColor(
i32
, i32
, i32
),
} fn main
() { let msg = Message::ChangeColor(
0
,
160
,
255
); match msg {
Message::Quit => { println!
(
"The Quit variant has no data to destructure."
)
}
Message::Move { x, y } => { println!
(
"Move in the x direction {} and in the y direction {}"
, x, y
);
}
Message::Write(text) => println!
(
"Text message: {}"
, text),
Message::ChangeColor(r, g, b) => println!
(
"Change the color to red {}, green {}, and blue {}"
, r, g, b
),
}
}
Для вариантов перечисления без каких-либо данных, вроде
Message::Quit
, мы не можем деструктурировать значение, которого нет. Мы можем сопоставить только буквальное значение
Message::Quit в этом шаблоне, но без переменных.
Для вариантов перечисления похожих на структуры, таких как
Message::Move
, можно использовать шаблон, подобный шаблону, который мы указываем для сопоставления структур. После имени варианта мы помещаем фигурные скобки и затем перечисляем поля именами переменных. Таким образом мы разделяем фрагменты, которые будут использоваться в коде этой ветки. Здесь мы используем сокращённую форму, как в листинге 18-13.
Для вариантов перечисления, подобных кортежу, вроде
Message::Write
, который содержит кортеж с одним элементом и
Message::ChangeColor
, содержащему кортеж с тремя элементами, шаблон аналогичен тому, который мы указываем для сопоставления кортежей. Количество переменных в шаблоне должно соответствовать количеству элементов в варианте, который мы сопоставляем.
Деструктуризация вложенных структур и перечислений
До сих пор все наши примеры сопоставляли структуры или перечисления на один уровень глубины, но сопоставление может работать и с вложенными элементами!
Например, мы можем реорганизовать код в листинге 18-15 для поддержки цветов RGB и
HSV в сообщении
ChangeColor
, как показано в листинге 18-16.
enum
Color
{
Rgb(
i32
, i32
, i32
),
Hsv(
i32
, i32
, i32
),
} enum
Message
{
Quit,
Move { x: i32
, y: i32
},
Write(
String
),
ChangeColor(Color),
} fn main
() { let msg = Message::ChangeColor(Color::Hsv(
0
,
160
,
255
)); match msg {
Message::ChangeColor(Color::Rgb(r, g, b)) => println!
(
"Change the color to red {}, green {}, and blue {}"
, r, g, b
),
Message::ChangeColor(Color::Hsv(h, s, v)) => println!
(
"Change the color to hue {}, saturation {}, and value {}"
, h, s, v
),
_ => (),
}
}
Листинг 18-16: Сопоставление со вложенными перечислениями
Шаблон первой ветки в выражении match соответствует варианту перечисления
Message::ChangeColor
, который содержит вариант
Color::Rgb
; затем шаблон привязывается к трём внутренними значениями i32
. Шаблон второй ветки также соответствует варианту перечисления
Message::ChangeColor
, но внутреннее перечисление соответствует варианту
Color::Hsv
. Мы можем указать эти сложные условия в одном выражении match
, даже если задействованы два перечисления.
Деструктуризация структур и кортежей
Можно смешивать, сопоставлять и вкладывать шаблоны деструктуризации ещё более сложными способами. В следующем примере показана сложная деструктуризация, где мы вкладываем структуры и кортежи внутрь кортежа и деструктурируем из него все примитивные значения:
Этот код позволяет нам разбивать сложные типы на составные части, чтобы мы могли использовать интересующие нас значения по отдельности.
Деструктуризация с помощью шаблонов - это удобный способ использования фрагментов значений, таких как как значение из каждого поля в структуре, по отдельности друг от друга.
Игнорирование значений в шаблоне
Вы видели, что иногда полезно игнорировать значения в шаблоне, например в последней ветке match
, чтобы получить ветку, обрабатывающую любые значения,
которая на самом деле ничего не делает, но учитывает все оставшиеся возможные значения. Есть несколько способов игнорировать целые значения или части значений в шаблоне: используя шаблон
_
(который вы видели), используя шаблон
_
внутри другого шаблона, используя имя, начинающееся с подчёркивания, либо используя
,
чтобы игнорировать оставшиеся части значения. Давайте рассмотрим, как и зачем использовать каждый из этих шаблонов.
Игнорирование всего значения с помощью шаблона _
Мы использовали подчёркивание (
_
) в качестве шаблона подстановочного знака
(wildcard), который будет сопоставляться с любом значением, но не будет привязываться к этому значению. Это особенно удобно в последней ветке выражения match
, но мы также можем использовать его в любом шаблоне, в том числе в параметрах функции, как показано в листинге 18-17.
let
((feet, inches), Point { x, y }) = ((
3
,
10
), Point { x:
3
, y: -
10
});
Файл: src/main.rs
Листинг 18-15: Использование
_
в сигнатуре функции
Этот код полностью игнорирует значение
3
, переданное в качестве первого аргумента,
и выведет на печать
This code only uses the y parameter: 4
В большинстве случаев, когда вам больше не нужен какой-то из параметров функции, вы можете изменить её сигнатуру, убрав неиспользуемый параметр. Игнорирование параметра функции может быть особенно полезно в случаях когда, например, вы реализуете типаж с определённой сигнатурой, но тело функции в вашей реализации не нуждается в одном из параметров. В таком случае компилятор не будет выдавать предупреждения о неиспользуемых параметрах функции, как это было бы, если бы вы указали имя параметра.
Игнорирование частей значения с помощью вложенного _
Также можно использовать
_
внутри другого шаблона, чтобы игнорировать только часть значения, например, когда мы хотим проверить только часть значения, не обращая внимания на другие его части, в соответствующем коде, который мы хотим выполнить. В листинге 18-18 показан код, отвечающий за управление значением настройки. Бизнес-требования заключаются в том, что пользователь не должен иметь права перезаписывать существующую настройку параметра, но может сбросить параметр и присвоить ему значение, если он в данный момент не установлен.
Листинг 18-18: Использование подчёркивания в шаблонах, соответствующих вариантам
Some
, когда нам
не нужно использовать значение внутри
Some fn foo
(_: i32
, y: i32
) { println!
(
"This code only uses the y parameter: {}"
, y);
} fn main
() { foo(
3
,
4
);
} let mut setting_value =
Some
(
5
); let new_setting_value =
Some
(
10
); match
(setting_value, new_setting_value) {
(
Some
(_),
Some
(_)) => { println!
(
"Can't overwrite an existing customized value"
);
}
_ => { setting_value = new_setting_value;
}
} println!
(
"setting is {:?}"
, setting_value);
Этот код будет печатать
Can't overwrite an existing customized value
, а затем setting is Some(5)
. В первой ветке нам не нужно сопоставлять или использовать значения внутри варианта
Some
, но нам нужно проверить случай, когда setting_value и new_setting_value являются вариантом
Some
. В этом случае мы печатаем причину,
почему мы не меняем значение setting_value и оно не меняется.
Во всех других случаях (если либо setting_value
, либо new_setting_value являются вариантом
None
), выраженных шаблоном
_
во второй ветке, мы хотим, чтобы new_setting_value стало равно setting_value
Мы также можем использовать подчёркивание в нескольких местах в одном шаблоне,
чтобы игнорировать конкретные значения. Листинг 18-19 показывает пример игнорирования второго и четвёртого значения в кортеже из пяти элементов.
Листинг 18-19: Игнорирование нескольких частей кортежа
Этот код напечатает
Some numbers: 2, 8, 32
, а значения 4 и 16 будут проигнорированы.
Игнорирование неиспользуемой переменной, начинающейся с символа _ в имени
Если вы создаёте переменную, но нигде её не используете, Rust обычно выдаёт предупреждение, потому что неиспользуемая переменная может быть ошибкой. Но иногда полезно создать переменную, которую вы пока не используете, например, когда вы создаёте прототип или только начинаете проект. В этой ситуации вы можете сказать
Rust не предупреждать вас о неиспользуемой переменной, начав имя переменной с подчёркивания. В листинге 18-20 мы создаём две неиспользуемые переменные, но когда мы компилируем такой код, мы должны получить предупреждение только об одной из них.
Файл: src/main.rs
Листинг 18-20: Начинаем имя переменной с подчёркивания, чтобы не получить предупреждения о
неиспользованных переменных
let numbers = (
2
,
4
,
8
,
16
,
32
); match numbers {
(first, _, third, _, fifth) => { println!
(
"Some numbers: {first}, {third}, {fifth}"
)
}
} fn main
() { let
_x =
5
; let y =
10
;
}