break and continue
break and continue steer a loop from inside its body. Bare forms act on the
innermost enclosing loop; the labeled forms target a named loop (see
Labeled control).
break
break; leaves the innermost loop immediately. Control resumes after the loop.
func:failsafe = int32(tbb32:err) {
exit 1;
};
func:main = int32() {
int32:x = 3i32;
while (x > 0i32) {
if (x == 1i32) { break; }
x -= 1i32;
}
exit x; // 1 — the loop stopped before decrementing to 0
};
continue
continue; skips the rest of the current iteration and jumps to the loop's
back-edge — the condition re-check for while, or the step/advance for the
counted forms.
func:main = int32() {
int32:total = 0i32;
loop(0i32, 5i32, 1i32) {
if ($ == 2i32) { continue; } // step still runs
total += $;
}
exit total; // 0 + 1 + 3 + 4 = 8
};
Note the difference across forms: in a counted loop or till, continue
still performs the step, so the loop makes progress. In a while, continue
re-checks the condition immediately — if you have not advanced the loop
variable before the continue, you can spin. Place your update before any
continue in a while:
while (i < n) {
i += 1i32; // advance first
if (skip(i)) { continue; }
work(i);
}
Transparency through if, blocks, and pick
break and continue see through if bodies, bare { } blocks, and pick
arms — they bind to the loop, not the nearest brace:
loop(0i32, 4i32, 1i32) {
pick ($) {
(2i32) { break; } // leaves the loop, not just the pick
(_) { fall; } // `fall` stays pick-scoped
}
}
fall is the one pick-scoped control word: it falls to the next pick arm and
never escapes to the loop. break and continue always target the loop.
The back-edge and invariants
A loop invariant is checked on the back-edge. Because break leaves before
the back-edge, it skips that check; continue reaches the back-edge and is
therefore still subject to it. See Invariants and contracts.
Next: Labeled control.