Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Циклы и другие прерываемые выражения

Syntax
LoopExpression
    LoopLabel? (
        InfiniteLoopExpression
      | PredicateLoopExpression
      | IteratorLoopExpression
      | LabelBlockExpression
    )

Rust поддерживает четыре типа выражений циклов:

  • Выражение loop обозначает бесконечный цикл.
  • Выражение while выполняется до тех пор, пока предикат не станет ложным.
  • Выражение for извлекает значения из итератора, выполняясь до опустошения итератора.
  • Выражение блока с меткой выполняет цикл ровно один раз, но позволяет досрочно выйти из цикла с помощью break.

Все четыре типа циклов поддерживают выражения break и метки.

Все, кроме выражений блоков с метками, поддерживают выражения continue.

Только выражения loop и блоки с метками поддерживают вычисление в нетривиальные значения.

Бесконечные циклы

Syntax
InfiniteLoopExpressionloop BlockExpression

Выражение loop повторяет выполнение своего тела непрерывно: loop { println!("I live."); }.

Выражение loop без связанного выражения break является расходящимся и имеет тип !.

Выражение loop, содержащее связанные выражения break, может завершаться и должно иметь тип, совместимый со значением выражений break.

Циклы с условием

Syntax
PredicateLoopExpressionwhile Conditions BlockExpression

Выражение цикла while позволяет повторять вычисление блока, пока набор условий остается истинным.

Синтаксис выражения while - это последовательность одного или более операндов условий, разделенных &&, за которыми следует BlockExpression.

Операнды условий должны быть либо Выражением с булевым типом, либо условным сопоставлением let. Если все операнды условий вычисляются в true и все образцы let успешно сопоставляются со своими рассматриваемыми выражениями, то выполняется блок тела цикла.

После успешного выполнения тела цикла операнды условий переоцениваются, чтобы определить, должно ли тело выполняться снова.

Если какой-либо операнд условия вычисляется в false или какой-либо образец let не сопоставляется со своим рассматриваемым выражением, тело не выполняется, и выполнение продолжается после выражения while.

Выражение while вычисляется в ().

Пример:

#![allow(unused)]
fn main() {
let mut i = 0;

while i < 10 {
    println!("hello");
    i = i + 1;
}
}

Образцы while let

Образцы let в условии while позволяют привязывать новые переменные в область видимости, когда образец успешно сопоставляется. Следующие примеры иллюстрируют привязки с использованием образцов let:

#![allow(unused)]
fn main() {
let mut x = vec![1, 2, 3];

while let Some(y) = x.pop() {
    println!("y = {}", y);
}

while let _ = 5 {
    println!("Неопровержимые образцы всегда истинны");
    break;
}
}

Цикл while let эквивалентен выражению loop, содержащему выражение match, следующим образом.

'label: while let PATS = EXPR {
    /* тело цикла */
}

эквивалентно

'label: loop {
    match EXPR {
        PATS => { /* тело цикла */ },
        _ => break,
    }
}

Несколько образцов могут быть указаны с оператором |. Это имеет ту же семантику, что и | в выражениях match:

#![allow(unused)]
fn main() {
let mut vals = vec![2, 3, 1, 2, 2];
while let Some(v @ 1) | Some(v @ 2) = vals.pop() {
    // Печатает 2, 2, затем 1
    println!("{}", v);
}
}

Цепочки условий while

Несколько операндов условий могут быть разделены с помощью &&. Они имеют ту же семантику и ограничения, что и цепочки условий if.

Следующий пример демонстрирует цепочку нескольких выражений, смешивая привязки let и булевы выражения, причем выражения могут ссылаться на привязки образцов из предыдущих выражений:

fn main() {
    let outer_opt = Some(Some(1i32));

    while let Some(inner_opt) = outer_opt
        && let Some(number) = inner_opt
        && number == 1
    {
        println!("Peek a boo");
        break;
    }
}

Циклы по итераторам

Syntax
IteratorLoopExpression
    for Pattern in Expressionexcept StructExpression BlockExpression

Выражение for - это синтаксическая конструкция для перебора элементов, предоставляемых реализацией std::iter::IntoIterator.

Если итератор выдает значение, это значение сопоставляется с неопровержимым образцом, выполняется тело цикла, а затем управление возвращается в начало цикла for. Если итератор пуст, выражение for завершается.

Пример цикла for по содержимому массива:

#![allow(unused)]
fn main() {
let v = &["apples", "cake", "coffee"];

for text in v {
    println!("I like {}.", text);
}
}

Пример цикла for по серии целых чисел:

#![allow(unused)]
fn main() {
let mut sum = 0;
for n in 1..11 {
    sum += n;
}
assert_eq!(sum, 55);
}

Цикл for эквивалентен выражению loop, содержащему выражение match, следующим образом:

'label: for PATTERN in iter_expr {
    /* тело цикла */
}

эквивалентно

{
    let result = match IntoIterator::into_iter(iter_expr) {
        mut iter => 'label: loop {
            let mut next;
            match Iterator::next(&mut iter) {
                Option::Some(val) => next = val,
                Option::None => break,
            };
            let PATTERN = next;
            let () = { /* тело цикла */ };
        },
    };
    result
}

IntoIterator, Iterator и Option здесь всегда являются элементами стандартной библиотеки, а не тем, на что эти имена разрешаются в текущей области видимости.

Имена переменных next, iter и val приведены для пояснения, они на самом деле не имеют имен, которые пользователь может набрать.

Note

Внешний match используется для обеспечения того, чтобы любые временные значения в iter_expr не были сброшены до завершения цикла. next объявляется до присваивания, потому что это чаще приводит к правильному выводу типов.

Метки циклов

Syntax
LoopLabelLIFETIME_OR_LABEL :

Выражение цикла может опционально иметь метку. Метка записывается как время жизни, предшествующее выражению цикла, как в 'foo: loop { break 'foo; }, 'bar: while false {}, 'humbug: for _ in 0..0 {}.

Если метка присутствует, то вложенные выражения break и continue с метками внутри этого цикла могут выйти из этого цикла или вернуть управление в его начало. См. выражения break и выражения continue.

Метки следуют правилам гигиены и затенения локальных переменных. Например, этот код напечатает “outer loop”:

#![allow(unused)]
fn main() {
'a: loop {
    'a: loop {
        break 'a;
    }
    print!("outer loop");
    break 'a;
}
}

'_ не является допустимой меткой цикла.

Выражения break

Syntax
BreakExpressionbreak LIFETIME_OR_LABEL? Expression?

Когда встречается break, выполнение связанного тела цикла немедленно прекращается, например:

#![allow(unused)]
fn main() {
let mut last = 0;
for x in 1..100 {
    if x > 12 {
        break;
    }
    last = x;
}
assert_eq!(last, 12);
}

Выражение break обычно связано с самым внутренним циклом loop, for или while, охватывающим выражение break, но метка может быть использована для указания, на какой охватывающий цикл влияет. Пример:

#![allow(unused)]
fn main() {
'outer: loop {
    while true {
        break 'outer;
    }
}
}

Выражение break разрешено только в теле цикла и имеет одну из форм break, break 'label или (см. ниже) break EXPR или break 'label EXPR.

Выражения блоков с метками

Syntax
LabelBlockExpressionBlockExpression

Выражения блоков с метками точно такие же, как обычные выражения блоков, за исключением того, что они позволяют использовать выражения break внутри блока.

В отличие от циклов, выражения break внутри выражения блока с меткой должны иметь метку (т.е. метка не является опциональной).

Аналогично, выражения блоков с метками должны начинаться с метки.

#![allow(unused)]
fn main() {
fn do_thing() {}
fn condition_not_met() -> bool { true }
fn do_next_thing() {}
fn do_last_thing() {}
let result = 'block: {
    do_thing();
    if condition_not_met() {
        break 'block 1;
    }
    do_next_thing();
    if condition_not_met() {
        break 'block 2;
    }
    do_last_thing();
    3
};
}

Выражения continue

Syntax
ContinueExpressioncontinue LIFETIME_OR_LABEL?

Когда встречается continue, текущая итерация связанного тела цикла немедленно прекращается, возвращая управление в начало цикла.

В случае цикла while началом являются операнды условий, управляющие циклом.

В случае цикла for началом является выражение-вызов, управляющее циклом.

Как и break, continue обычно связан с самым внутренним охватывающим циклом, но continue 'label может быть использован для указания затронутого цикла.

Выражение continue разрешено только в теле цикла.

break и значения циклов

При связи с циклом loop выражение break может быть использовано для возврата значения из этого цикла с помощью одной из форм break EXPR или break 'label EXPR, где EXPR - это выражение, результат которого возвращается из loop. Например:

#![allow(unused)]
fn main() {
let (mut a, mut b) = (1, 1);
let result = loop {
    if b > 10 {
        break b;
    }
    let c = a + b;
    a = b;
    b = c;
};
// первое число в последовательности Фибоначчи больше 10:
assert_eq!(result, 13);
}

В случае, если у loop есть связанный break, он не считается расходящимся, и loop должен иметь тип, совместимый с каждым выражением break. break без выражения считается идентичным break с выражением ().