Loop Constructs in Move
Many programs require iteration over values, and Move provides while
and loop
forms to allow you
to write code in these situations. In addition, you can also modify control flow of these loops
during execution by using break
(to exit the loop) and continue
(to skip the remainder of this
iteration and return to the top of the control flow structure).
while
Loops
The while
construct repeats the body (an expression of type unit) until the condition (an
expression of type bool
) evaluates to false
.
Here is an example of simple while
loop that computes the sum of the numbers from 1
to n
:
fun sum(n: u64): u64 {
let mut sum = 0;
let mut i = 1;
while (i <= n) {
sum = sum + i;
i = i + 1
};
sum
}
Infinite while
loops are also allowed:
fun foo() {
while (true) { }
}
Using break
Inside of while
Loops
In Move, while
loops can use break
to exit early. For example, suppose we were looking for the
position of a value in a vector, and would like to break
if we find it:
fun find_position(values: &vector<u64>, target_value: u64): Option<u64> {
let size = vector::length(values);
let mut i = 0;
let mut found = false;
while (i < size) {
if (vector::borrow(values, i) == &target_value) {
found = true;
break
};
i = i + 1
};
if (found) {
Option::Some(i)
} else {
Option::None
}
}
Here, if the borrowed vector value is equal to our target value, we set the found
flag to true
and then call break
, which will cause the program to exit the loop.
Finally, note that break
for while
loops cannot take a value: while
loops always return the
unit type ()
and thus break
does, too.
Using continue
Inside of while
Loops
Similar to break
, Move's while
loops can invoke continue
to skip over part of the loop body.
This allows us to skip part of a computation if a condition is not met, such as in the following
example:
fun sum_even(values: &vector<u64>): u64 {
let size = vector::length(values);
let mut i = 0;
let mut even_sum = 0;
while (i < size) {
let number = *vector::borrow(values, i);
i = i + 1;
if (number % 2 == 1) continue;
even_sum = even_sum + number;
};
even_sum
}
This code will iterate over the provided vector. For each entry, if that entry is an even number, it
will add it to the even_sum
. If it is not, however, it will call continue
, skipping the sum
operation and returning to the while
loop conditional check.
loop
Expressions
The loop
expression repeats the loop body (an expression with type ()
) until it hits a break
:
fun sum(n: u64): u64 {
let mut sum = 0;
let mut i = 1;
loop {
i = i + 1;
if (i >= n) break;
sum = sum + i;
};
sum
}
Without a break
, the loop will continue forever. In the example below, the program will run
forever because the loop
does not have a break
:
fun foo() {
let mut i = 0;
loop { i = i + 1 }
}
Here is an example that uses loop
to write the sum
function:
fun sum(n: u64): u64 {
let sum = 0;
let i = 0;
loop {
i = i + 1;
if (i > n) break;
sum = sum + i
};
sum
}
Using break
with Values in loop
Unlike while
loops, which always return ()
, a loop
may return a value using break
. In doing
so, the overall loop
expression evaluates to a value of that type. For example, we can rewrite
find_position
from above using loop
and break
, immediately returning the index if we find it:
fun find_position(values: &vector<u64>, target_value: u64): Option<u64> {
let size = vector::length(values);
let mut i = 0;
loop {
if (vector::borrow(values, i) == &target_value) {
break Option::Some(i)
} else if (i >= size) {
break Option::None
};
i = i + 1;
}
}
This loop will break with an option result, and, as the last expression in the function body, will produce that value as the final function result.
Using continue
Inside of loop
Expressions
As you might expect, continue
can also be used inside a loop
. Here is the previous sum_even
function rewritten using loop
with break
and continue
instead of while
.
fun sum_even(values: &vector<u64>): u64 {
let size = vector::length(values);
let mut i = 0;
let mut even_sum = 0;
loop {
if (i >= size) break;
let number = *vector::borrow(values, i);
i = i + 1;
if (number % 2 == 1) continue;
even_sum = even_sum + number;
};
even_sum
}
The Type of while
and loop
In Move, loops are typed expressions. A while
expression always has type ()
.
let () = while (i < 10) { i = i + 1 };
If a loop
contains a break
, the expression has the type of the break. A break with no value has
the unit type ()
.
(loop { if (i < 10) i = i + 1 else break }: ());
let () = loop { if (i < 10) i = i + 1 else break };
let x: u64 = loop { if (i < 10) i = i + 1 else break 5 };
let x: u64 = loop { if (i < 10) { i = i + 1; continue} else break 5 };
In addition, if a loop contains multiple breaks, they must all return the same type:
// invalid -- first break returns (), second returns 5
let x: u64 = loop { if (i < 10) break else break 5 };
If loop
does not have a break
, loop
can have any type much like return
, abort
, break
,
and continue
.
(loop (): u64);
(loop (): address);
(loop (): &vector<vector<u8>>);
If you need even more-precise control flow, such as breaking out of nested loops, the next chapter presents the use of labeled control flow in Move.