Loops

Loops allow you to execute code repeatedly, either a specific number of times or while a condition remains true. Conjure supports both classic while loops and iterator focused for-in style iteration, with plans to expand loop syntax based on real-world usage patterns.

while

The while loop continues executing its body as long as the given condition evaluates to true. The condition is checked before each iteration, so if it is false initially, the loop body will not execute at all.

var shouldQuit = false

while !shouldQuit {
    performAction()
}

If no condition is provided, the loop will run indefinitely until a break statement is encountered.

while { // equivalent to `while true`
	if shouldStop() {
		break
	}
	performAction()
}

for-in

The for-in loop iterates over ranges, arrays, slices, and other iterable types.

for i in 0..5 {
    c.printf("%d\n", i)  // prints 0, 1, 2, 3, 4, 5
}

The loop variable i is scoped to the loop body and cannot be accessed outside of it.

Iterating Over Ranges Proposed

Range literals work seamlessly with for-in loops. Use inclusive ranges with ... or exclusive ranges with .. depending on your needs. See Basic Expressions: Range Literals for more details.

// Exclusive range (right side excluded)
for i in 0..10 {
    c.printf("%d ", i)  // prints 0 1 2 3 4 5 6 7 8 9
}

// Inclusive range (both sides included) with custom increment
for i in 10...4 by 2 {
    c.printf("%d ", i)  // prints 10 8 6 4
}

Iterating Over Arrays

Arrays can be iterated directly, giving you each element in sequence. When iterating over an array, the loop variable takes on the type of the array’s elements.

var numbers = [5]i32{10, 20, 30, 40, 50}

for num in numbers {
    c.printf("%d\n", num)
}

To access both the index and value during iteration, specify two loop variables separated by a comma. The first variable receives the index, and the second receives the value. This works with any iterable type.

var colors = [3]string{"red", "green", "blue"}

for i, color in colors {
    c.printf("Color %d: %s\n", i, color)
}

Ignoring Values Proposed

If you don’t need the index or the value, use an underscore to ignore it.

// Ignore the index, only use the value
for _, item in items {
    process(item)
}

// Ignore the value, only use the index
for i, _ in items {
    c.printf("Processing item %d\n", i)
}

// Just iterate a specific number of times
for _ in 0..10 {
    performAction()
}

Iterating Over Custom Types Proposed

Custom types can implement iterator interfaces to work with for-in loops. The standard library provides iterator support for common collections like lists, maps, and sets. See the conjure/overloads module for details.

var map = HashMap[string, i32]{}
map.set("a", 1)
map.set("b", 2)

for key, value in map {
    c.printf("%s: %d\n", key, value)
}

Breaking Out of Loops

The break statement immediately exits the loop. When loops are nested, break only exits the innermost loop.

for i in 0..100 {
    if i > 50 {
        break
    }
    c.printf("%d ", i)
}

Continuing to Next Iteration

The continue statement skips the rest of the current iteration and moves to the next one.

for i in 0..10 {
    if i % 2 == 0 {
        continue  // skip even numbers
    }
    c.printf("%d ", i)  // prints 1 3 5 7 9
}

Labeled Breaks and Continues Proposed

When working with nested loops, you can label loops and break or continue to specific ones. This is particularly useful for complex nested iteration where you need to exit multiple levels at once.

For developers looking to avoid the usage of goto and maintain readable code with nested loops that obey expected control flow semantics (including defer), Conjure supports labeled breaks and continues.

outer: for x in 0..5 {
    inner: for y in 0..5 {
        if x * y > 10 {
            break inner  // breaks out of the inner loop
        }

		if x + y > 6 {
			continue outer  // continues the next iteration of the outer loop
		}

        c.printf("(%d, %d) ", x, y)
    }
}

Loop Inlining Proposed

Conjure plans to support compile-time loop inlining for any iterable type. This allows loops over compile-time known ranges to be completely unrolled. This is powerful for performance-critical code where you want zero loop overhead.

import "@debug"

func main() {
	@for i in 0...5 {
		debug.log(i) // remains a runtime log
	}
}

// Expands to:
debug.log(0)
debug.log(1)
debug.log(2)
debug.log(3)
debug.log(4)