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.

Copyright (c) 2025 Ocean Softworks, Sharkk