Loops
Loops are straightforward in Coco—we've got for and while, and they do exactly what you'd expect. No surprises, just clean syntax.
For Loops
The for loop iterates over anything that implements the Iterable trait:
// Iterate over an array
$fruits = ["apple", "banana", "cherry"];
for $fruit in $fruits {
echo $fruit;
}
// Iterate over a range
for $i in 0..5 {
echo $i; // 0, 1, 2, 3, 4
}
// Inclusive range
for $i in 0..=5 {
echo $i; // 0, 1, 2, 3, 4, 5
}
Ranges
Ranges are first-class values in Coco:
// Exclusive range (doesn't include end)
0..10 // 0 through 9
// Inclusive range
0..=10 // 0 through 10
// Step ranges
(0..10).step_by(2) // 0, 2, 4, 6, 8
// Reverse
(0..10).rev() // 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
You can use ranges anywhere, not just in loops:
$digits = 0..10;
$contains_five = $digits.contains(5); // true
While Loops
Loop while a condition is true:
$count = 0;
while $count < 5 {
echo $count;
$count += 1;
}
Loop Control
Break
Exit a loop early:
for $i in 0..100 {
if $i == 10 {
break;
}
echo $i;
}
// Prints 0 through 9
Continue
Skip to the next iteration:
for $i in 0..10 {
if $i % 2 == 0 {
continue; // Skip even numbers
}
echo $i;
}
// Prints 1, 3, 5, 7, 9
Labels
For nested loops, use labels to break or continue the outer loop:
'outer: for $i in 0..3 {
for $j in 0..3 {
if $i == 1 && $j == 1 {
break 'outer; // Break out of both loops
}
echo "({$i}, {$j})";
}
}
Loop with Index
When you need both the index and value:
$fruits = ["apple", "banana", "cherry"];
for ($i, $fruit) in $fruits.enumerate() {
echo "{$i}: {$fruit}";
}
// 0: apple
// 1: banana
// 2: cherry
Iterating with Mutation
To modify elements while iterating:
$numbers = [1, 2, 3, 4, 5];
for $n in &$numbers {
$n = 2;
}
// numbers is now [2, 4, 6, 8, 10]
Iterating Over Maps
$scores = {
"Alice": 95,
"Bob": 87,
"Charlie": 92
};
// Keys and values
for ($name, $score) in $scores {
echo "{$name}: {$score}";
}
// Just keys
for $name in $scores.keys() {
echo $name;
}
// Just values
for $score in $scores.values() {
echo $score;
}
Loops with Error Handling
Coco has nice patterns for handling errors in loops:
// Retry pattern
for $attempt in 0..3 {
$result = http_get($url) catch $err {
if $attempt == 2 {
return error.wrap($err, "Failed after 3 attempts");
}
echo "Attempt {$attempt + 1} failed, retrying...";
continue;
};
// Success
return Ok($result);
}
Collecting Results
When each iteration can fail:
$urls = ["url1", "url2", "url3"];
$results = [];
for $url in $urls {
$data = fetch($url) catch $err {
echo "Failed to fetch {$url}: {$err}";
continue;
};
$results.push($data);
}
Or fail the whole operation on first error:
$urls = ["url1", "url2", "url3"];
$results = [];
for $url in $urls {
$data = fetch($url); // Auto-propagates error
$results.push($data);
}
Infinite Loops
Use while true for intentional infinite loops:
// Event loop
while true {
$event = wait_for_event();
match $event {
Event::Quit => break,
Event::Input($data) => process($data),
default => {}
}
}
Performance Notes
For loops are zero-cost. The compiler optimizes them just like you'd hand-write in C.
Prefer iterators for transforms. Instead of:
$results = [];
for $item in $items {
if $item.is_valid() {
$results.push($item.transform());
}
}
Use iterator chains:
$results = $items
.iter()
.filter(($x) { $x.is_valid() })
.map(($x) { $x.transform() })
.collect();
Both compile to similar code, but the iterator version is often clearer and enables better optimizations.
Common Patterns
Find First Match
$found = null;
for $item in $items {
if $item.matches($criteria) {
$found = $item;
break;
}
}
// Or use iterators
$found = $items.iter().find(($x) { $x.matches($criteria) });
Process in Batches
for $batch in $items.chunks(100) {
process_batch($batch);
}
Parallel Iteration
$names = ["Alice", "Bob", "Charlie"];
$scores = [95, 87, 92];
for ($name, $score) in $names.zip($scores) {
echo "{$name}: {$score}";
}
Early Exit with Value
$result = 'search: {
for $item in $items {
if $item.matches($criteria) {
break 'search Some($item);
}
}
None
};
Best Practices
Use descriptive loop variables. $user is better than $u, $item is better than $i (unless it's actually an index).
Prefer iterators when transforming data. They're more declarative and often more efficient.
Avoid mutating what you're iterating over. If you need to remove items while iterating, collect indices first or use retain().
Keep loop bodies short. If a loop body gets long, extract it into a function.