Поток управления
Ирма Окунева · наставница
Горн не печка, его не включают один раз. Каждый такт он ест корзину угля и отдаёт жар, а без угля остывает. Пока не научишься считать такты, металл будет то сырым, то сожжённым. И смотри на цвет: тёмно-красный ещё рано, вишнёвый пора, белый поздно.
Такт за тактом это цикл, а цвет по температуре это выбор из нескольких диапазонов. В этом уроке разберём три цикла 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, и почему строка из первого урока «переезжала», а число на её месте скопировалось бы молча.