Loop
Learn about loop in Move, which are used to control program flow.
Loop
Move offers multiple constructs for looping: while, loop, and for. These constructs allow you to repeat code execution based on conditions or iterate over ranges and collections.
While Loops
The while construct repeats the body (an expression of type unit) until the condition (an expression of type bool) evaluates to false.
Basic While Loop
Here is an example of a simple while loop that calculates the product of numbers from 1 to n (factorial):
fun factorial(n: u64): u64 {
let result = 1;
let i = 1;
while (i <= n) {
result *= i;
i += 1;
};
result
}Infinite Loops
Infinite loops are allowed but should be used with caution due to computational cost:
fun foo() {
while (true) { }
}Break Statement
The break expression can be used to exit a loop before the condition evaluates to false. For example, this loop uses break to find the first power of 2 that exceeds a given threshold:
fun first_power_exceeding(threshold: u64): u64 {
let power = 1;
let exponent = 0;
while (true) {
if (power > threshold) break;
power *= 2;
exponent += 1;
};
power
}The break expression cannot be used outside of a loop.
Continue Statement
The continue expression skips the rest of the loop and continues to the next iteration. This loop uses continue to count only odd numbers from 1 to n:
fun count_odd_numbers(n: u64): u64 {
let count = 0;
let i = 0;
while (i < n) {
i += 1;
if (i % 2 == 0) continue;
count += 1;
};
count
}The continue expression cannot be used outside of a loop.
The Type of Break and Continue
break and continue, much like return and abort, can have any type. The following examples illustrate where this flexible typing can be helpful:
fun merge_until_duplicate(
v1: vector<u8>,
v2: vector<u8>,
): vector<u8> {
let result = vector::empty();
while (!vector::is_empty(&v1) && !vector::is_empty(&v2)) {
let val1 = *vector::borrow(&v1, 0);
let val2 = *vector::borrow(&v2, 0);
let next_val =
if (val1 < val2) vector::remove(&mut v1, 0)
else if (val2 < val1) vector::remove(&mut v2, 0)
else break; // Here, `break` has type `u8`
vector::push_back(&mut result, next_val);
};
result
}fun collect_valid_strings(
lengths: vector<u64>,
v1: &vector<vector<u8>>,
v2: &vector<vector<u8>>
): vector<vector<u8>> {
let len1 = vector::length(v1);
let len2 = vector::length(v2);
let result = vector::empty();
while (!vector::is_empty(&lengths)) {
let target_len = vector::pop_back(&mut lengths);
let chosen_vector =
if (target_len <= len1) v1
else if (target_len <= len2) v2
else continue; // Here, `continue` has type `&vector<vector<u8>>`
vector::push_back(&mut result, *vector::borrow(chosen_vector, target_len - 1))
};
result
}The Loop Expression
The loop expression repeats the loop body (an expression with type ()) until it hits a break.
Without a break, the loop will continue forever:
fun infinite_counter() {
let counter = 0;
loop { counter = counter + 1 }
}Loop with Break
Here is an example that uses loop to calculate the greatest common divisor (GCD):
fun gcd(a: u64, b: u64): u64 {
let x = a;
let y = b;
loop {
if (y == 0) break;
let temp = y;
y = x % y;
x = temp
};
x
}Loop with Continue
As you might expect, continue can also be used inside a loop. Here is an example that counts prime numbers up to n:
fun count_primes(n: u64): u64 {
let count = 0;
let num = 2;
loop {
if (num > n) break;
if (!is_prime(num)) {
num = num + 1;
continue;
};
count = count + 1;
num = num + 1
};
count
}
fun is_prime(n: u64): bool {
if (n < 2) return false;
let i = 2;
while (i * i <= n) {
if (n % i == 0) return false;
i = i + 1
};
true
}The Type of While and Loop
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 type unit ():
(loop { if (i < 10) i = i + 1 else break }: ());
let () = loop { if (i < 10) i = i + 1 else break };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>>);For Loops
For loops are used to iterate over a range of values, providing a more concise syntax for common iteration patterns.
Basic For Loop Syntax
for (i in 1..n) {
// code to be executed
}Range Iteration
For loops can iterate over numeric ranges:
fun product_range(start: u64, end: u64): u64 {
let product = 1;
for (i in start..end) {
product *= i;
};
product
}Vector Iteration
For loops can iterate over vector indices:
fun find_minimum(v: &vector<u64>): u64 {
let min_val = *vector::borrow(v, 0);
for (i in 1..vector::length(v)) {
let current = *vector::borrow(v, i);
if (current < min_val) {
min_val = current;
};
};
min_val
}For Loop with Break and Continue
break and continue work in for loops just like in while loops:
fun find_first_perfect_square(start: u64, end: u64): u64 {
for (i in start..end) {
let sqrt_i = integer_sqrt(i);
if (sqrt_i * sqrt_i != i) continue;
return i
};
abort 1 // No perfect square found
}
fun multiply_until_overflow(start: u64, end: u64, threshold: u64): u64 {
let product = 1;
for (i in start..end) {
if (product > threshold / i) break; // Prevent overflow
product = product * i;
};
product
}
fun integer_sqrt(n: u64): u64 {
if (n == 0) return 0;
let x = n;
let y = (x + 1) / 2;
while (y < x) {
x = y;
y = (x + n / x) / 2;
};
x
}Practical Examples
Vector Processing
fun find_second_largest_index(v: &vector<u64>): u64 {
let largest = 0;
let second_largest = 0;
let second_idx = 0;
for (i in 0..vector::length(v)) {
let val = *vector::borrow(v, i);
if (val > largest) {
second_largest = largest;
second_idx = if (largest > 0) i - 1 else 0;
largest = val;
} else if (val > second_largest && val < largest) {
second_largest = val;
second_idx = i;
};
};
second_idx
}Nested Loops
fun matrix_diagonal_product(matrix: &vector<vector<u64>>): u64 {
let product = 1;
let size = vector::length(matrix);
for (i in 0..size) {
let row = vector::borrow(matrix, i);
if (i < vector::length(row)) {
let diagonal_val = *vector::borrow(row, i);
product = product * diagonal_val;
};
};
product
}Fibonacci Calculation
fun fibonacci(n: u64): u64 {
if (n <= 1) return n;
let prev = 0;
let curr = 1;
for (i in 2..=n) {
let next = prev + curr;
prev = curr;
curr = next;
};
curr
}Performance Considerations
-
Choose the right loop type:
- Use
forloops for known ranges - Use
whileloops for condition-based iteration - Use
loopfor infinite loops with explicit breaks
- Use
-
Minimize work inside loops:
// Inefficient: repeated calculation for (i in 0..n) { let expensive_value = expensive_calculation(); process(i, expensive_value); }; // Efficient: calculate once let expensive_value = expensive_calculation(); for (i in 0..n) { process(i, expensive_value); }; -
Use early termination when possible with
break
Best Practices
- Use descriptive loop variables -
i,j,kfor simple counters, meaningful names for complex logic - Avoid infinite loops unless specifically needed
- Use
forloops for ranges - more readable than manual while loop counters - Consider vector iteration patterns - use appropriate vector functions when possible
- Handle edge cases - empty vectors, zero ranges, etc.
Common Patterns
Accumulator Pattern
fun sum_of_squares(v: &vector<u64>): u64 {
let sum = 0;
for (i in 0..vector::length(v)) {
let val = *vector::borrow(v, i);
sum = sum + (val * val);
};
sum
}Search Pattern
fun find_last_occurrence(v: &vector<u64>, target: u64): u64 {
let last_index = vector::length(v); // Use length as "not found" indicator
for (i in 0..vector::length(v)) {
if (*vector::borrow(v, i) == target) {
last_index = i;
};
};
last_index
}Filter Pattern
fun filter_multiples_of_three(v: &vector<u64>): vector<u64> {
let result = vector::empty();
for (i in 0..vector::length(v)) {
let val = *vector::borrow(v, i);
if (val % 3 == 0 && val > 0) {
vector::push_back(&mut result, val);
};
};
result
}Summary
Move provides three loop constructs for repetitive execution and iteration:
while: Condition-based loops that repeat while a boolean condition is trueloop: Infinite loops that require explicitbreakstatements to exitfor: Range-based iteration over numeric ranges withstart..endsyntax- Control flow: Use
breakto exit loops early andcontinueto skip to next iteration - Performance: Choose the right loop type and minimize work inside loop bodies
Loops enable efficient iteration patterns while maintaining Move's safety guarantees.