Главная / Курсы / Rust / Глава 4. Метки
# Глава 4. Метки При использовании вложенных циклов бывает необходимость прервать внешний цикл по условию из внутреннего. Для этого в Rust были добавлены метки. Имя метки должно начинаться с апострофа `'` (одинарная кавычка). Синтаксис определения метки: ```rust '<label name>: <expression> ``` Для перехода на метки используются операторы `continue` и `break`. Например: ```rust 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 '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 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 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 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 безопасно.
Отправка...

Если вам нравится проект, вы можете поддержать его!

Задонатить