# Глава 4. Метки
При использовании вложенных циклов бывает необходимость прервать внешний цикл по условию из внутреннего. Для этого в Rust были добавлены метки. Имя метки должно начинаться с апострофа `'` (одинарная кавычка). Синтаксис определения метки:
```rust
'<label name>: <expression>
```
Для перехода на метки используются операторы `continue` и `break`. Например:
```rust {.example_for_playground .example_for_playground_001}
fn main() {
let mut i = 1;
'outer: loop {
println!("[outer] step {i}");
'inner: for n in 1..i + 1 {
if n == 2 {
println!("[inner] skip step {n}");
continue 'inner;
} else if n > 2 {
println!("break loops at step {n}");
break 'outer;
}
println!("[inner] step {n}");
}
i += 1;
}
}
```
Программа напечатает:
```
[outer] step 1
[inner] step 1
[outer] step 2
[inner] step 1
[inner] skip step 2
[outer] step 3
[inner] step 1
[inner] skip step 2
break loops at step 3
```
## Оператор continue
Оператор `continue` прерывает текущую итерацию цикла и осуществляет переход на указанную метку либо на новую итерацию цикла, если метка не задана. Синтаксис оператора `continue`:
```rust
continue [label name];
```
Этот код напечатает все двузначные числа, в записи которых обе цифры четные (22, 24,.. 86, 88):
```rust {.example_for_playground .example_for_playground_002}
'outer: for a in 1..10 {
'inner: for b in 1..10 {
// продолжает цикл по a
if a % 2 != 0 {
continue 'outer;
}
// продолжает цикл по b
if b % 2 != 0 {
continue 'inner;
}
println!("{a}{b}");
}
}
```
В примере метка `'inner` использована для наглядности, здесь она не обязательна. Операторы `continue` и `break` без метки всегда относятся к текущему циклу. Поэтому если убрать метку `'inner`, то логика работы не изменится.
## Оператор break
Оператор `break` прерывает цикл и осуществляет переход на метку, если она указана. С помощью `break` можно вернуть значение, которое будет результирующим для прерванного выражения. Синтаксис оператора `break`:
```rust
break [label name] [expression value];
```
Важный момент: область видимости метки ограничена выражением, с которым она связана. Попытка использовать метку вне ее выражения приведет к ошибке компиляции.
По задумке функция `calc_sum()` вычисляет и печатает сумму элементов заданной матрицы. Если какой-то из элементов меньше нуля, то расчет прекращается и выводится сообщение об ошибке:
```rust {.example_for_playground .example_for_playground_003}
fn calc_sum(matrix: &Vec<Vec<i32>>) {
println!("start calculation");
let mut sum: i32 = 0;
for row in matrix {
for item in row {
if *item < 0 {
println!("error: input data is corrupted");
break 'exit;
}
sum += *item;
}
}
println!("sum = {}", sum);
'exit: {
println!("stop calculation");
}
}
```
Матрица `matrix` определена как вектор векторов `Vec<Vec<i32>>`. Вектор — это массив изменяемого размера. Таким образом вектор векторов `Vec<Vec<i32>>` образует двумерный массив с элементами типа `i32`. Переменная `item` является ссылкой `&i32`, поэтому в выражениях используется разыменование `*item`. Компиляция функции `calc_sum()` завершится с ошибкой:
```
error[E0426]: use of undeclared label `'exit`
--> src\main.rs:9:23
|
9 | break 'exit;
| ^^^^^ undeclared label `'exit`
warning: unused label
--> src\main.rs:16:5
|
16 | 'exit: {
| ^^^^^
|
= note: `#[warn(unused_labels)]` on by default
```
Компилятор видит две разные метки `'exit`, так как они связаны c разными выражениями. Первая метка используется во внутреннем цикле, но не объявлена в нем либо выше. Вторая метка объявлена в блоке, печатающем сообщение об ошибке, но не используется в нем.
Для исправления функции `calc_sum()` необходимо использовать метку `'exit` в блоке, с которым она связана. Например, это можно сделать так:
```rust {.example_for_playground .example_for_playground_004}
fn calc_sum(matrix: &Vec<Vec<i32>>) {
println!("start calculation");
let mut sum: i32 = 0;
if 'exit: {
for row in matrix {
for item in row {
if *item < 0 {
break 'exit false;
}
sum += *item;
}
}
true
} {
println!("sum = {}", sum);
} else {
println!("error: input data is corrupted");
}
println!("stop calculation");
}
```
Здесь выражение метки `'exit` используется в условии. В зависимости от истинности или ложности выражения метки будет напечатано сумма всех элементов либо сообщение об ошибке. Вывод для матрицы содержащей отрицательное число:
```
start calculation
error: input data is corrupted
stop calculation
```
Необходимо реализовать функцию `check()`, которая проверяет, что все элементы матрицы входят в указанный диапазон. Функция возвращает `false`, если какой-то элемент матрицы меньше нижней границы диапазона `min` или больше верхней границы диапазона `max`. В противном случае функция возвращает `true`: {.task_text}
```rust {.task_source #rust_chapter_0040_task_0010}
fn check(matrix: &Vec<Vec<i32>>, min: i32, max: i32) -> bool {
// добавьте реализацию функции
}
```
Для обхода матрицы следует использовать двойной цикл `for`. При нахождении элемента матрицы, не входящего в диапазон `[min..max]`, необходимо прервать оба цикла. Используя `break` с меткой, реализовать выход из двойного цикла. {.task_hint}
```rust {.task_answer}
fn check(matrix: &Vec<Vec<i32>>, min: i32, max: i32) -> bool {
'stop: {
for row in matrix {
for item in row {
if *item < min || *item > max {
break 'stop false;
}
}
}
true
}
}
```
## Составные выражения с меткой
Cвязанное с меткой выражение не обязано содержать цикл. Это может быть любое составное выражение. Например, такой код является корректным:
```rust {.example_for_playground .example_for_playground_005}
let value: u32 = 0;
let value: u32 = 'init: {
println!("1: {}", value);
if value == 0 {
break 'init 10;
}
value
};
println!("2: {}", value);
```
Здесь метка `'init` связана с блоком кода, инициализирующим вторую переменную `value` (которая [затеняет](/courses/rust/chapters/rust_chapter_0020#block-shadowing) первую), поэтому метка видна только в этом блоке. При инициализации второй переменной `value` еще доступна первая, поэтому условие `value == 0` будет истинным. Блок инициализации прервется на `break 'init 10` и `value` получит значение 10. Программа выведет:
```
1: 0
2: 10
```
В отличие от языков C и C++ использование меток в Rust безопасно, так как применение меток ограничено выражением, с которым они связаны. Основная функция меток — прерывание исполнения вложенных циклов.
## Заключение
- Имя метки начинается с апострофа `'`. При объявлении метка связывается с конкретным составным выражением: `'label: {...}`.
- Для перехода на метку используются операторы `continue` и `break`.
- Оператор `continue` позволяет пропустить текущую итерацию цикла.
- Оператор `break` применяется для прерывания текущего цикла. Для прерывания нескольких вложенных циклов требуется использовать `break` с меткой.
- С помощью `break` можно задать возвращаемое значение цикла.
- Область видимости метки ограничена выражением, с которым она связана. Благодаря этому использование меток в Rust безопасно.
Наша группа в telegram. Здесь можно задавать вопросы и общаться.
Задонатить. Если вам нравится курс, вы можете поддержать развитие площадки!