The Basics
while runs its body as long as a bool condition stays true:
while condition {
// body
}
The compiler evaluates the condition before each iteration:
true— run the body, then check again.false— exit the loop.
A trivial illustration from Zero's examples, with a condition that's already false:
Run that and only the second write fires. The body of the while never executes because the condition was false the first time it was checked.
The Condition Is a bool
As with if/else, the loop condition has to be a bool. Zero doesn't coerce integers, strings, or other values into a boolean.
while count { // compile error
// ...
}
while count > 0 { // OK
// ...
}
The condition can be anything that evaluates to bool: a binding, a comparison, a &&/|| combination, or a function call. The same rules from if apply.
Counted Loops
The classic "do something N times" pattern uses a counter and a while:
let mut i = 0
while i < 10 {
// do work
i = i + 1
}
Three pieces: an initial value, a condition that compares against the bound, and an update in the body. This is the pattern every imperative language has — Zero just doesn't dress it up in a for keyword.
A note on the mut spelling above: how Zero spells mutable bindings in your toolchain may differ in pre-1.0 versions. Run zero check --json against a small test to confirm the exact syntax your compiler accepts. The conceptual pattern (counter + condition + update) is the part that's stable.
Infinite Loops
A while whose condition is always true runs forever:
while true {
// wait for work, handle it, loop again
}
This is the right shape for an event loop, a REPL, or a long-running server. To exit, you either terminate the process, raise an error out of the body, or restructure to break the condition.
When to Reach for a Loop
Loops are a tool to use sparingly. Things that look like loops in other languages often have a cleaner shape in Zero:
- Iterating over a fixed collection: prefer a function that the standard library or your shape exposes — a
forEach-style helper, a folder, or a recursive walk. - Reading until end-of-input: loop on a fallible read, but use
checkandraisesto handle the boundary cleanly rather than nesting status flags. - Polling for a condition: consider whether the design should hand you a signal instead. Polling loops are a smell in any systems language.
The same instinct applies to agents: a tight, declarative shape is easier to reason about than a hand-written counter loop, both for humans and for code generators.
Loops and Effects
A while body, like any other block, can do I/O — but only if it has access to a World capability (or a slice of one). A function whose signature doesn't mention World can loop all it wants but can't write anything to the outside. That property holds inside the loop body too; the loop doesn't grant any new capabilities.
This sounds obvious, but it's the reason you can put a while inside a "pure" computation function and still know — from the signature alone — that it can't print anything or write to disk.
Style Notes
A few small habits that pay off:
- Keep the condition obvious. If the condition is doing real work, lift it into a named function or binding so the loop reads cleanly.
- Update the counter at the bottom of the body, not scattered through it. Easier to spot off-by-one bugs.
- Prefer early-exit conditions you can express as the loop's own condition over flags that flip mid-iteration. Fewer moving parts.
Next: Shapes
You've now seen Zero's core control flow. The next chapter is about modeling data — starting with shapes, Zero's struct-like product types.
Frequently Asked Questions
How do while loops work in Zero?
Use while condition { ... }. The condition is evaluated before each iteration; if it's true, the body runs and the loop checks again. If it's false, the loop exits and execution continues after the closing brace. The condition has to be a bool.
Does Zero have a for loop?
Early Zero ships while as its only loop construct. The language deliberately keeps the surface small while it stabilizes — fewer keywords means fewer ways for an agent to pick the wrong form. A for over a range or collection may land later; until then, you build the same pattern with while and a counter.
How do you write a counted loop in Zero?
Initialize a counter, run a while against it, and update inside the body: let mut i = 0; while i < 10 { ...; i = i + 1 }. Mutability syntax is still evolving in pre-1.0 Zero, so check the current docs for the exact spelling — but the pattern of while + counter + update is the standard counted-loop idiom.
Is there a break or continue in Zero?
Most languages in Zero's family ship some early-exit construct for loops; the exact spelling in pre-1.0 Zero is one of the areas that may move before 1.0. The conservative approach is to structure the loop so the condition itself does the work — invert and update the condition so the loop naturally exits — rather than relying on a specific control-flow keyword.
Can a Zero while loop run forever?
Yes — while true { ... } is an infinite loop. Useful for servers, event loops, REPLs, anything where you don't have a natural termination condition. Unlike implicit truthiness, true here is a literal of type bool so the condition is still well-typed.