Loops
Rux has four loops. Each runs a block repeatedly; they differ in when, or whether, they test a condition.
| Loop | Tests | The body runs |
|---|---|---|
while c { … } | before each pass | zero or more times |
do { … } while c; | after each pass | at least once |
loop { … } | never | until a break or return |
for x in s { … } | for a next element of s | once per element |
while-loop = [label ":"] "while" condition block
do-while = [label ":"] "do" block "while" condition ";"
loop = [label ":"] "loop" block
for-loop = [label ":"] "for" Name "in" expression block
Any loop may carry a label, so that a break or continue inside a nested loop can name it — see break and continue. Conditions follow the rules in Statements: a bool, no parentheses needed, a structure literal wrapped in parentheses.
while
while tests its condition before every pass, including the first, so the body may never run:
var i = 0;
while i < 3 {
PrintLine("{}", i);
i++;
}
do while
do … while runs the body first and tests afterwards, so the body always runs at least once. The statement ends with ;:
var tries = 5;
do {
tries--;
} while tries > 10;
The condition is false from the start, but tries is still decremented once, to 4. A continue in the body goes straight to the test.
loop
loop repeats until something leaves it — a break, a return, a fail, or a panic. It says "until I stop it" more directly than while true:
var n = 0;
loop {
n += 1;
if n == 4 {
break;
}
}
for
for runs its body once for each element of a sequence, binding the element to the loop variable:
let values = [10, 20, 30];
for v in values {
PrintLine("{}", v);
}
The subject is evaluated once, before the first pass. What for can iterate:
| Subject | Elements |
|---|---|
a fixed-size array T[N] | each element, in order |
a slice T[..] | each element, in order |
a string literal or other char8[..] | each char8 code unit — a byte of UTF-8, not a character |
a range a..b, a..=b, a...b | each integer from a, up to b exclusive or inclusive |
an open range a.. | each integer from a, until the loop is left |
| a type with an iterator | each value its iterator's Next returns |
for k in 0..3 {
Print("{} ", k);
}
for k in 0..=3 {
Print("{} ", k);
}
This prints 0 1 2 0 1 2 3. A range whose start is past its end runs zero times. When both ends are constants, the compiler catches it instead: for k in 5..2 is error: range start cannot be greater than its end. See Ranges.
Any other type is iterated through the iterator convention: a container declares a parameterless Iterate returning an iterator, and the iterator declares func Next(self: &var Iterator) -> Item?. Each pass calls Next once; a present result is the element, and none ends the loop. See Iteration.
for left in countdown {
Print("{} ", left);
}
A type with neither is refused:
error: cannot iterate over 'int'
help: iterate an array, a slice, a range, or a type declaring 'Next' or 'Iterate'
The loop variable
The loop variable is a single name, fresh on every pass and immutable: i = 10; inside for i in 0..3 is error: cannot modify immutable variable 'i'. It cannot be declared var, and it cannot be a pattern — for (a, b) in pairs does not parse. Bind a mutable copy or take an element apart inside the body:
for pair in pairs {
let (a, b) = pair;
PrintLine("{} {}", a, b);
}
Moving inside a loop
A loop body runs again from where its last pass ended, so a value one pass moves out is not there for the next. Moving an outer variable inside a loop is an error unless the same pass gives it a new value before the loop repeats, or leaves the loop after the move:
for i in 0..3 {
Consume(<- buffer);
}
error: value 'buffer' may have been moved on some control-flow paths
The loop variable, and bindings declared inside the body, are new on every pass and may be moved freely. See Copy and move.