Loops

GX has loops for every situation — counting, iterating, filtering, and even building new data from loops.

While Loop

Repeat while a condition is true:

fn main() {
    var count = 0

    while (count < 5) {
        print("count = {count}\n")
        count++
    }
    print("Done!\n")
}

For-Range Loop

Count through a range of numbers. The range is inclusive on both ends:

fn main() {
    // Prints 1, 2, 3, 4, 5
    for (i = 1:5) {
        print("i = {i}\n")
    }
}

for (i = 1:5) means “i goes from 1 to 5, including 5.” No off-by-one headaches.

fn main() {
    // Countdown
    for (i = 10:1) {
        print("{i}... ")
    }
    print("Liftoff!\n")
}

For-Each Loop

Iterate over every element in an array:

fn main() {
    var fruits:str[4] = {"apple", "banana", "cherry", "date"}

    for (var fruit in fruits) {
        print("I like {fruit}\n")
    }
}

Works with any array type:

fn main() {
    var scores:i32[5] = {92, 87, 95, 78, 88}
    var total = 0

    for (var s in scores) {
        total += s
    }

    print("Total: {total}\n")
    print("Average: {total / 5}\n")
}

Filtered Iteration with where

Add a condition to skip elements:

fn main() {
    var nums:i32[8] = {1, 2, 3, 4, 5, 6, 7, 8}

    print("Even numbers: ")
    for (var n in nums) where (n % 2 == 0) {
        print("{n} ")
    }
    print("\n")

    print("Greater than 5: ")
    for (var n in nums) where (n > 5) {
        print("{n} ")
    }
    print("\n")
}

No temporary arrays, no extra allocations — where filters inline.

Collect: Build Arrays from Loops

collect captures loop results into a new dynamic array:

fn main() {
    var nums:i32[8] = {1, 2, 3, 4, 5, 6, 7, 8}

    // Collect even numbers
    var evens = for (n in nums) where (n % 2 == 0) collect n

    print("Evens ({evens.len()}):")
    for (i = 0:evens.len() - 1) {
        print(" {evens.at(i)}")
    }
    print("\n")

    // Collect with transformation
    var doubled = for (n in nums) where (n <= 4) collect n * 2

    print("Doubled ({doubled.len()}):")
    for (i = 0:doubled.len() - 1) {
        print(" {doubled.at(i)}")
    }
    print("\n")

    evens.free()
    doubled.free()
}

Break and Continue

Control loop flow:

fn main() {
    // break: exit the loop early
    print("Break example: ")
    for (i = 1:10) {
        if (i == 6) {
            break
        }
        print("{i} ")
    }
    print("\n")  // prints: 1 2 3 4 5

    // continue: skip to next iteration
    print("Continue example: ")
    for (i = 1:10) {
        if (i % 3 == 0) {
            continue
        }
        print("{i} ")
    }
    print("\n")  // prints: 1 2 4 5 7 8 10
}

Practical Example: Finding a Value

fn main() {
    var data:i32[6] = {14, 27, 3, 91, 42, 8}
    var target = 42
    var found = false

    for (var val in data) {
        if (val == target) {
            found = true
            break
        }
    }

    if (found) {
        print("Found {target}!\n")
    } else {
        print("{target} not found\n")
    }
}

Try it — Write a loop with where and collect in the Playground.


Expert Corner

For-range is inclusive (for (i = 1:5) gives 1,2,3,4,5). This was a deliberate design choice — inclusive ranges are more intuitive for mathematical sequences, game loops (for (frame = 0:59) gives you 60 frames), and human-readable code. No < n vs <= n confusion.

collect is not magic — it’s a compile-time transform. The compiler generates a dynamic array with .init() and .push() calls inside the loop. You’re responsible for calling .free() on the result when you’re done. This keeps the language honest about allocations.

where filters happen inline with zero overhead — no intermediate array is created. The compiler emits a simple if check inside the loop body. It’s syntactic sugar that reads like English without costing performance.

par for exists for parallel iteration across CPU cores. It’s the same syntax with a par prefix: par for (i = 0:999) { ... }. The compiler generates threaded work distribution. That’s a topic for an advanced tutorial.