Раздел 23 · Rust

Поток управления: циклы и match

junior~15 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

Поток управления

Ирма Окунева · наставница

Горн не печка, его не включают один раз. Каждый такт он ест корзину угля и отдаёт жар, а без угля остывает. Пока не научишься считать такты, металл будет то сырым, то сожжённым. И смотри на цвет: тёмно-красный ещё рано, вишнёвый пора, белый поздно.

Такт за тактом это цикл, а цвет по температуре это выбор из нескольких диапазонов. В этом уроке разберём три цикла Rust, метки для вложенных циклов и match, который сравнивает значение с диапазонами и не даёт забыть ни один случай.

Циклы

В Rust есть три цикла: loop, while и for. Разберём их по порядку, от самого простого к самому частому.

Цикл loop

loop создаёт бесконечный цикл: он крутится, пока его не остановит break.

Особенность loop: break может вернуть значение. Тогда весь цикл становится выражением, и это значение его результат:

let mut attempt = 0;
let delay = loop {
    attempt += 1;
    let backoff = 100 * 2_u32.pow(attempt);
    if backoff > 1000 {
        break backoff;  // Возвращаем значение и выходим из цикла
    }
};

Это идиома для поиска: цикл крутится, пока не найдёт нужное, и возвращает найденное.

Цикл while

while это классический цикл с условием. Он крутится, пока условие истинно:

let mut countdown = 5;
while countdown > 0 {
    println!("{countdown}");
    countdown -= 1;
}

Пока countdown больше нуля, печатаем его значение и уменьшаем на единицу.

Цикл for

В Rust цикл for работает только с итерируемыми значениями: например, с диапазонами или коллекциями. В отличие от C, здесь нет цикла с тремя секциями (for (i=0; i<n; i++)).

Примеры использования:

// Перебираем диапазон чисел (0..5 не включает 5)
for i in 0..5 {
    println!("{i}");  // Выведет: 0, 1, 2, 3, 4
}

// Перебираем элементы массива
for day in days {
    println!("{day}");
}

Важные нюансы про диапазоны:

  • 0..5: диапазон от 0 до 4 (правая граница не включается);
  • 0..=5: диапазон от 0 до 5 (правая граница включается).

Почему for часто лучше ручной индексации?

  • Безопаснее: итератор сам знает, где кончается коллекция, поэтому выйти за пределы массива нельзя.
  • Быстрее: не нужно проверять границы на каждом шаге.
  • Короче: нет счётчика, который можно забыть увеличить.

Под капотом for работает с итераторами, им посвящён отдельный урок.

Метки для вложенных циклов

Когда у тебя несколько вложенных циклов, может понадобиться выйти сразу из внешнего цикла. В других языках для этого часто используют флаг found и проверяют его на каждом уровне. В Rust для этого есть метки:

'search: for row in 0..height {
    for col in 0..width {
        if grid[row][col] == target {
            break 'search;  // Выходим сразу из внешнего цикла с меткой 'search
        }
    }
}

Одна команда break 'search заменяет целую систему флагов и проверок, и код становится короче и понятнее.

match по диапазонам

Полное знакомство с pattern matching (сопоставлением с образцом) ждёт нас в уроке про перечисления, но одну конструкцию разберём уже сейчас: она нужна и горну, и домашнему заданию.

match сравнивает значение с шаблонами и выполняет код подошедшей ветви:

let label = match score {
    0 => "ноль",
    1..=49 => "мало",
    50..=99 => "почти",
    _ => "сотня или больше",
};

Как это работает?

  • match это выражение, он возвращает значение (в нашем примере строку);
  • ветви проверяются сверху вниз: выполняется код первого подошедшего шаблона;
  • символ _ служит «заглушкой», которая ловит все оставшиеся случаи (аналог default в switch).

Важные особенности:

  • Компилятор проверяет, что все возможные случаи учтены (это называется исчерпываемость). Если какой‑то случай пропущен, код не скомпилируется.
  • Шаблоны бывают не только числовыми: match разбирает и кортежи, и перечисления, и структуры.

Если ты работал с Gleam или Haskell, синтаксис покажется тебе знакомым. В уроке про перечисления match покажет, на что он ещё способен.

Практика

Теперь руками. Перед тобой семь задач на этот урок и на прошлый: типы, if как выражение, shadowing и циклы. Решай их прямо в браузере, от лёгкой разминки к заданиям посложнее.

1. Разминка: if как выражение
Используй if как выражение, которое возвращает значение, а не как оператор.

2. Границы целых типов
Вспомни таблицу с типами чисел и проверь, как ведут себя границы целых типов на практике.

3. Циклы и арифметика
Примени на практике всё, что узнал о циклах: от loop до for.

4. Работа с shadowing
Преобразуй значение по шагам, перекрывая одно и то же имя через shadowing.

5. Факториал
Классическая задача на цикл и арифметику.

6. Последовательность Коллатца
Посчитай шаги последовательности Коллатца: здесь пригодятся и цикл, и условие.

7. Преобразование типов (as) и биты
Финальное задание: разберись, что делает оператор as с битами. Сначала попробуй предсказать результат работы каждой функции, а затем проверь себя с помощью тестов.

Мастерская, этап 3

Горн из прошлого урока уже считает уголь. Теперь он греется тактами: цикл гоняет тик, а match по диапазонам называет цвет каления. Код прошлого этапа переедет сюда сам.

Доска заказов

На доске Гильдии висит заказ без подсказок: такты Вадима.

ДЗ

Дальше

Горн работает: такт за тактом, цвет по диапазону. Пора плавить металл в слитки, а у слитка, как скажет Ферапонт, всегда один хозяин. В следующем уроке мы погрузимся в сердце языка и разберём владение (ownership): что такое move и copy, как работают стек и куча, что такое drop и RAII, и почему строка из первого урока «переезжала», а число на её месте скопировалось бы молча.