Циклы

На данный момент в Rust есть три способа организовать циклическое исполнение кода. Это loop, while и for. У каждого подхода своё применения.

Циклы loop

Бесконечный цикл (loop) — простейшая форма цикла в Rust. С помощью этого ключевого слова можно организовать цикл, который продолжается, пока не выполнится какой-либо оператор, прерывающий его. Бесконечный цикл в Rust выглядит так:

fn main() { loop { println!("Зациклились!"); } }
loop {
    println!("Зациклились!");
}

Циклы while

Цикл while — это ещё один вид конструкции цикла в Rust. Выглядит он так:

fn main() { let mut x = 5; // mut x: i32 let mut done = false; // mut done: bool while !done { x += x - 3; println!("{}", x); if x % 5 == 0 { done = true; } } }
let mut x = 5; // mut x: i32
let mut done = false; // mut done: bool

while !done {
    x += x - 3;

    println!("{}", x);

    if x % 5 == 0 {
        done = true;
    }
}

Он применяется, если неизвестно, сколько раз нужно выполнить тело цикла, чтобы получить результат. При каждой итерации цикла проверяется условие, и если оно истинно, то запускается следующая итерация. Иначе цикл while завершается.

Если вам нужен бесконечный цикл, то можете сделать условие всегда истинным:

fn main() { while true { }
while true {

Однако, для такого случая в Rust имеется ключевое слово loop:

fn main() { loop { }
loop {

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

Циклы for

Цикл for нужен для повторения блока кода определённое количество раз. Циклы for в Rust работают немного иначе, чем в других языках программирования. Например в Си-подобном языке цикл for выглядит так:

for (x = 0; x < 10; x++) {
    printf( "%d\n", x );
}

Однако, этот код в Rust будет выглядеть следующим образом:

fn main() { for x in 0..10 { println!("{}", x); // x: i32 } }
for x in 0..10 {
    println!("{}", x); // x: i32
}

Можно представить цикл более абстрактно:

fn main() { for переменная in выражение { тело_цикла } }
for переменная in выражение {
    тело_цикла
}

Выражение — это итератор. Их мы будем рассматривать позже в этом руководстве. Итератор возвращает серию элементов, где каждый элемент будет являться одной итерацией цикла. Значение этого элемента затем присваивается переменной, которая будет доступна в теле цикла. После окончания тела цикла, берётся следующее значение итератора и снова выполняется тело цикла. Когда в итераторе закончатся значения, цикл for завершается.

В нашем примере, 0..10 — это выражение, которое задаёт начальное и конечное значение, и возвращает итератор. Обратите внимание, что конечное значение не включается в него. В нашем примере будут напечатаны числа от 0 до 9, но не будет напечатано 10.

В Rust намеренно нет цикла for в стиле C. Управлять каждым элементом цикла вручную сложно, и это может приводить к ошибкам даже у опытных программистов на C.

Перечисление

Если вы хотите отслеживать число прошедших итераций, используйте функцию .enumerate().

С интервалами

fn main() { for (i,j) in (5..10).enumerate() { println!("i = {} и j = {}", i, j); } }
for (i,j) in (5..10).enumerate() {
    println!("i = {} и j = {}", i, j);
}

Выводит:

i = 0 и j = 5
i = 1 и j = 6
i = 2 и j = 7
i = 3 и j = 8
i = 4 и j = 9

Не забудьте написать скобки вокруг интервала.

С итераторами

fn main() { let lines = "привет\nмир\nhello\nworld".lines(); for (linenumber, line) in lines.enumerate() { println!("{}: {}", linenumber, line); } }
for (linenumber, line) in lines.enumerate() {
    println!("{}: {}", linenumber, line);
}

Outputs:

0: привет
1: мир
2: hello
3: world

Раннее прерывание цикла

Давайте ещё раз посмотрим на цикл while:

fn main() { let mut x = 5; let mut done = false; while !done { x += x - 3; println!("{}", x); if x % 5 == 0 { done = true; } } }
let mut x = 5;
let mut done = false;

while !done {
    x += x - 3;

    println!("{}", x);

    if x % 5 == 0 {
        done = true;
    }
}

В этом примере в условии для выхода из цикла используется изменяемое имя done логического типа. В Rust имеются два ключевых слова, которые помогают работать с итерациями цикла: break и continue.

Мы можем переписать цикл с помощью break, чтобы избавиться от переменной done:

fn main() { let mut x = 5; loop { x += x - 3; println!("{}", x); if x % 5 == 0 { break; } } }
let mut x = 5;

loop {
    x += x - 3;

    println!("{}", x);

    if x % 5 == 0 { break; }
}

Теперь мы используем бесконечный цикл loop и break для выхода из цикла. Использование явного return также остановит выполнение цикла.

continue похож на break, но вместо выхода из цикла переходит к следующей итерации. Следующий пример отобразит только нечётные числа:

fn main() { for x in 0..10 { if x % 2 == 0 { continue; } println!("{}", x); } }
for x in 0..10 {
    if x % 2 == 0 { continue; }

    println!("{}", x);
}

Метки циклов

Когда у вас много вложенных циклов, вы можете захотеть указать, к какому именно циклу относится break или continue. Как и во многих других языках, по умолчанию эти операторы будут относиться к самому внутреннему циклу. Если вы хотите прервать внешний цикл, вы можете использовать метку. Так, этот код будет печатать на экране только когда и x, и y нечётны:

fn main() { 'outer: for x in 0..10 { 'inner: for y in 0..10 { if x % 2 == 0 { continue 'outer; } // продолжает цикл по x if y % 2 == 0 { continue 'inner; } // продолжает цикл по y println!("x: {}, y: {}", x, y); } } }
'outer: for x in 0..10 {
    'inner: for y in 0..10 {
        if x % 2 == 0 { continue 'outer; } // продолжает цикл по x
        if y % 2 == 0 { continue 'inner; } // продолжает цикл по y
        println!("x: {}, y: {}", x, y);
    }
}