How do guards work in a do/while loop, in both statement and expression form?
← Control Flow · Ref: Q1355
The do/while loop accepts a guard in its header (after 'do'), and — like the other loops — it has an expression form that yields a value. The guard is checked once at entry; if it leaves the guard variable unset the body is skipped, even though a do/while normally runs its body at least once.
DO/WHILE STATEMENT WITH A ?= GUARD
first as String? more <- true do first ?= fetchRecord() stdout.println(`got ${first}`) more: false while more
The guarded assignment (?=) assigns fetchRecord() to 'first' and checks it is SET before the body runs.
DO/WHILE EXPRESSION WITH A :=? GUARD
The expression form declares a returning variable as the first body line and yields its final value:
cached as String? loopOnce <- true summary <- do cached :=? fetchRecord() <- rtn as String: "none" rtn: cached loopOnce: false while loopOnce
Assign-if-unset (:=?) only assigns when 'cached' is currently unset. If the guard leaves it unset, the body is skipped and 'summary' is the initial value "none".
GUARD POSITION
The guard is the pre-flow, written directly after 'do'; the loop control stays where it belongs, after the body in the trailing 'while' clause.
See Q67 for the plain do/while. See Q77 for while guards. See Q1354 for the while expression form with a guard. See Q1361 for guards used uniformly across all expression-form constructs.
Example
defines module qa.controlflow.guarddowhile defines function fetchRecord() <- rtn as String: "record-A" defines program GuardDoWhileDemo() stdout <- Stdout() // do/while STATEMENT with a ?= guard (guard checked once, after 'do') first as String? more <- true do first ?= fetchRecord() stdout.println(`got ${first}`) more: false while more // do/while EXPRESSION with a :=? assign-if-unset guard cached as String? loopOnce <- true summary <- do cached :=? fetchRecord() <- rtn as String: "none" rtn: cached loopOnce: false while loopOnce stdout.println(`summary is ${summary}`)
Common mistakes
E02001 — The guard belongs in the header, directly after 'do' (do first ?= fetchRecord()), not as the first statement of the body. As a guard it can skip the whole body when unset. See ek9 -h E02001.
Incorrect:
do first ?= fetchRecord() stdout.println(`got ${first}`) while more
Correct:
do first ?= fetchRecord() stdout.println(`got ${first}`) more: false while more
Other ways to ask this
- Can a do-while loop use a guard variable?
- How do I use ?= or :=? in a do/while loop?
- do-while as an expression with a guard
- guarded assignment in a do while loop
Coming from another language?
Java: do-while is a statement only; it always runs the body once and cannot gate the first iteration on a guarded, loop-scoped variable, nor produce a value. You would use a nullable variable plus a manual check. Kotlin/Swift/Rust: do-while (or repeat-while) is likewise a statement with no guard binding and no value form. EK9: do/while accepts <-, ?= and :=? guards that can skip even the first iteration when unset, and has an expression form that yields the returning variable.
Keywords: returning, isset, flow, while, guarded, :=?, control, expression, ?=, loop, do-while, do, guard