# Циклы и диапазоны в Rust: loop, while и for на практике

> Пошаговый гайд по циклам в Rust: синтаксис loop, while, for, работа с диапазонами range, итераторами и досрочным выходом break/continue с примерами.

- **URL:** https://eagle2000.space/rust/rust-loops/
- **Date:** 2026-07-31

- **Tags:** rust, dev, Rust-basic

---## Введение

В языке Rust есть три основных вида циклов: `while`, `loop` и `for`. Для управления итерациями используются стандартные операторы:
* **`break`** — немедленно прерывает выполнение цикла.
* **`continue`** — пропускает оставшуюся часть тела цикла и переходит к следующей итерации.

Давайте детально рассмотрим все виды циклов, способы обхода коллекций и работу с метками на интерактивных слайдах:


### 💻 Циклы в Rust


#### Шаг 1

```rust
fn main() {
    let mut n = 0;
    // Цикл while
    while n < 3 {
        println!("while: n = {}", n);
        n += 1;
    }

    // Цикл loop с возвратом значения через break
    let mut counter = 0;
    let result = loop {
        counter += 1;
        if counter == 5 {
            break counter * 2; // Возвращает 10 из цикла loop
        }
    };
    println!("Результат loop: {}", result);

    // Симуляция do-while через loop
    let mut x = 0;
    loop {
        x += 1;
        println!("do-while: x = {}", x);
        if x >= 3 {
            break;
        }
    }
}
```

**Циклы while, loop и возврат значений**
- `while` выполняет тело, пока истинно логическое условие.
- `loop` представляет собой бесконечный цикл.
- Выражение `loop` может возвращать значение наружу: для этого нужно указать его после оператора `break`.
- Отсутствующий в Rust цикл `do-while` можно сымитировать проверкой условия выхода в конце бесконечного `loop`.


#### Шаг 2

```rust
fn main() {
    let arr = [10, 20, 30];

    // Итерация по ссылкам (&arr не потребляет коллекцию)
    for val in &arr {
        println!("Ссылка: {}", val);
    }

    // Итерация с получением индекса через .enumerate()
    for (idx, val) in arr.into_iter().enumerate() {
        println!("Индекс {}: элемент {}", idx, val);
    }

    let mut mut_arr = [1, 2, 3];
    // Изменяемая итерация для модификации элементов
    for val in &mut mut_arr {
        *val *= 10;
    }
    println!("Измененный массив: {:?}", mut_arr);
}
```

**Диапазоны, цикл for и итерация**
- Цикл `for` итерируется по объектам, реализующим типаж `IntoIterator`.
- `for item in &arr` выполняет неизменяемое заимствование (итерируется по ссылкам `&T`).
- `for item in &mut arr` позволяет изменять элементы на месте.
- Метод `.enumerate()` возвращает кортеж `(index, item)` для одновременного доступа к счетчику и элементу.


#### Шаг 3

```rust
fn main() {
    // Метки циклов позволяют управлять внешними циклами из вложенных
    'outer: for i in 1..=3 {
        for j in 1..=3 {
            if i * j > 4 {
                println!("Выход из внешнего цикла при i={}, j={}", i, j);
                break 'outer;
            }
        }
    }

    // Метки именованных блоков кода
    let val = 'tag: {
        let x = 10;
        if x > 5 {
            break 'tag 100; // Досрочный выход из блока с возвратом значения 100
        }
        x
    };
    println!("Значение из блока: {}", val);
}
```

**Метки циклов и блоков (Labels)**
- Метки начинаются с апострофа (например, `'outer`) и позволяют управлять внешними циклами с помощью `break` или `continue`.
- Обычные блоки кода `{}` также могут иметь метки. Из них можно досрочно выйти с помощью `break 'tag` и вернуть значение.
- Использование `break` без метки внутри обычного блока кода (не цикла) вызовет ошибку компиляции.


---

## Виды циклов в Rust

### 1. Цикл `while`

Выполняет тело цикла до тех пор, пока условие истинно (`true`).
```rust
let mut n = 0;
while n < 10 {
    foo(n);
    n += 1;
}
```

### 2. Цикл `while let`

Повторяет выполнение цикла, пока выражение успешно сопоставляется с шаблоном (например, для разбора `Option` или `Result`).
```rust
// Цикл работает, пока foo() возвращает Some(x)
while let Some(x) = foo() {
    bar(x);
}
```

### 3. Цикл `loop` и возврат значений

Бесконечный цикл, который выполняется до тех пор, пока не будет встречен явный оператор `break`.
* **Использование как выражения:** `loop` может возвращать значение через оператор `break`. Это значение можно присвоить переменной.

```rust
let event = loop {
    let event = get_event();
    if let Some(e) = event {
        break e; // Возвращает значение e из цикла loop
    }
};
```

### 4. Реализация цикла `do ... while`

В Rust нет встроенного ключевого слова `do-while`. Его поведение можно сымитировать двумя способами:
1. **Через бесконечный `loop` и проверку условия в конце:**
```rust
loop {
    let x = foo();
    if x == 0 { break; }
}
```
2. **Используя блок кода в качестве условия цикла `while`:**
```rust
while {
    let x = foo();
    x != 0 // Условие продолжения цикла
} {} // Пустое тело цикла
```

---

## Диапазоны (Ranges) и цикл `for`

### Типы диапазонов (Range Types)

Диапазоны представляют собой структуры из модуля `std::ops`. Нижняя граница (если она указана) **всегда включается** в диапазон.

| Синтаксис | Описание типа | Включение границ | Внутреннее представление |
| --- | --- | --- | --- |
| `..` | Неограниченный диапазон | Нет границ | `RangeFull` |
| `..10` | Ограниченный сверху диапазон | Исключая верхнюю границу (0-9) | `RangeTo` |
| `..=10` | Ограниченный сверху диапазон, включительно | Включая верхнюю границу (0-10) | `RangeToInclusive` |
| `0..` | Ограниченный снизу диапазон | От 0 до бесконечности | `RangeFrom` |
| `0..10` | Ограниченный диапазон | Включая 0, исключая 10 (0-9) | `Range` |
| `0..=10` | Ограниченный диапазон, включительно | Включая обе границы 0 и 10 (0-10) | `RangeInclusive` |

*Пример:* Цикл `for n in 1..10` выполнится ровно **9 раз** (для значений от 1 до 9 включительно).

### Анатомия цикла `for`

Цикл `for` используется для итерации по элементам коллекции или диапазона.
* **Требование к типу:** Выражение справа от `in` обязано реализовывать типаж **`std::iter::IntoIterator`**.

В зависимости от необходимости владения данными или ссылками, применяются разные подходы:
1. **Потребление коллекции (Move):** `for item in arr { ... }` (неявно вызывает `arr.into_iter()`).
2. **Итерация по разделяемым ссылкам (`&T`):** `for item in &arr { ... }` (неявно вызывает `(&arr).into_iter()`).
3. **Итерация по изменяемым ссылкам (`&mut T`):** `for item in &mut arr { ... }` (неявно вызывает `(&mut arr).into_iter()`).

### Получение индекса при итерации (`.enumerate()`)

Для одновременного получения индекса элемента и самого элемента используется метод `.enumerate()`, который возвращает кортеж вида `(index, item)`.

```rust
let arr: [u8; 3] = [10, 20, 30];
for (idx, item) in arr.into_iter().enumerate() {
    println!("{idx}: {item}");
}
```

---

## Метки циклов и блоков (Labels)

Метки позволяют управлять выполнением конкретного внешнего цикла или блока изнутри вложенных структур. Названия меток всегда начинаются с одиночной кавычки (апострофа), например: `'label`.

### Метки в циклах

Применяются для выхода из внешнего цикла при нахождении во вложенном.

```rust
'outer: loop {
    'inner: loop {
        if need_exit() {
            break 'outer; // Полный выход из внешнего цикла
        }
    }
}
```

### Метки в именованных блоках кода

Обычные блоки кода `{ ... }` также могут иметь метки. Это позволяет досрочно прерывать выполнение текущего блока и возвращать значение во внешнюю переменную с помощью `break`.

```rust
let y = 'tag: {
    let x = foo();
    if x < 0 {
        break 'tag 0; // Досрочный выход из блока с возвратом значения 0
    }
    x
};
```

> **Ограничение компилятора:** Оператор `break` без указания явной метки внутри обычного обособленного блока кода (не являющегося циклом) вызовет **ошибку компиляции**.

---

## Проверь свои знания!

Пройдите короткий интерактивный тест, чтобы закрепить понимание циклов в Rust.


### 📝 Циклы в Rust


**1. Каким образом бесконечный цикл `loop` может возвращать значение наружу?**

- [ ] С помощью ключевого слова `return`
- [ ] Указав значение после оператора `break` (например, `break value;`)
- [ ] Цикл loop не умеет возвращать значения
- [ ] Просто оставив выражение без точки с запятой в конце тела цикла

**2. Сколько раз выполнится тело цикла `for i in 1..5 { ... }`?**

- [ ] 5 раз (для 1, 2, 3, 4, 5)
- [ ] 4 раза (для 1, 2, 3, 4)
- [ ] 6 раз (для 0, 1, 2, 3, 4, 5)
- [ ] Ни разу, так как синтаксис некорректен

**3. Какой типаж (trait) обязано реализовывать выражение после ключевого слова `in` в цикле `for`?**

- [ ] std::iter::Iterator
- [ ] std::iter::IntoIterator
- [ ] std::clone::Clone
- [ ] std::ops::Index

**4. Для чего используется метод `.enumerate()` при итерации?**

- [ ] Для сортировки элементов по возрастанию
- [ ] Для получения пар вида (индекс, элемент) на каждой итерации
- [ ] Для фильтрации дубликатов в коллекции
- [ ] Для преобразования всех элементов к строковому типу

**5. Какое из утверждений о метках (labels) в Rust является неверным?**

- [ ] Названия меток всегда начинаются с одиночной кавычки (например, `'outer`)
- [ ] Метки можно ставить как на циклы, так и на обычные блоки кода `{}`
- [ ] Из именованного блока кода `{}` можно досрочно выйти с помощью `break 'label`
- [ ] Оператор `break` без указания метки можно использовать для выхода из обычного обособленного блока кода `{}`

