RFA-607 · Case file with fixtures · Case 579 of 694 · Compiler evidence
Rust continue Can Target Only a Loop, Not a Labelled Block
Continue means begin the next iteration of a loop. Use break for a labelled block's early result, or make repetition an explicit loop with a semantic label.
- Reviewed
- Rust
- Rust 1.98.1, edition 2024
- Targets
- all Rust targets
- Profiles
- dev, release, test
Direct answer
What this Rust failure means
- Why it happens
- A label was mistaken for sufficient repetition semantics even though only loop expressions define another iteration to continue.
- First discriminating check
- Use a labelled break for one-shot early exit or introduce a real loop with progress and termination evidence when retrying is intended.
continue means stop the current iteration and begin the next iteration of a loop. A labelled block executes once and has no next iteration. The failing fixture targets such a block and rustc reports E0696.
Labels do not make every construct repeatable
A label gives control flow a name; the underlying construct still determines which operations make sense. Loops accept continue because they define repeated iterations. Labelled blocks accept break 'label, optionally with a result, because they define an early-exit scope.
The official E0696 explanation shows that both labelled and unlabelled continue forms are invalid when their destination is a block rather than a loop.
I inspect the construct carrying the label, not only whether the spelling exists.
The repair makes retrying a real loop
The repaired fixture defines 'retry: loop, increments an attempt counter, continues once, and then breaks. Its assertion proves the second iteration happened.
If the original code did not need repetition, break 'decision value would be the correct block-level operation instead. Converting every block to a loop simply to preserve continue can create accidental infinite loops.
I name what repetition represents: retries, records, connections, or pages. That makes continue 'pages communicate which unit is skipped when loops are nested.
Continue runs loop-tail behaviour differently by loop form
In a for loop, continue advances through the iterator machinery to the next item. In a while loop, it returns to condition evaluation. In a bare loop, it restarts the body. Side effects placed after the continue are skipped.
The Reference defines continue expressions. I pay attention to counters, backoff sleeps, cleanup, and metrics that a continue may bypass.
A manual retry loop should not skip delay or cancellation checks accidentally. Often I structure one iteration as a function returning a typed decision, then the loop centrally applies sleep, accounting, and termination policy.
Loops and blocks return values differently
A labelled block can return a value through labelled break. A bare loop can return a value through break as well. continue itself never completes the construct with a value; it abandons the remainder of the current iteration.
For state machines, an enum such as Step::Retry, Step::Complete(value), or Step::Fail(error) can make transitions more testable than several labels. Labels remain excellent for small lexical nesting where the destination is obvious.
I avoid hidden mutation around control-flow keywords. Values returned from a loop keep result ownership local, while explicit counters and budgets document progress and termination.
Retry loops need termination evidence
Once E0696 is fixed by adding a loop, I verify that some path must break or return. The compiler permits intentional infinite loops, so it cannot prove business termination.
Retry tests cover success, permanent failure, budget exhaustion, cancellation, and backoff. I use a fake clock rather than sleeping. For iterators, tests cover skipped items and ensure cleanup or acknowledgements happen at the intended point.
Nested loops need inputs where continuing inner and outer loops produce different counts. This catches a correct label attached to the wrong semantic operation.
In stream consumers, continue can also decide whether an item is acknowledged. I place acknowledgement policy outside incidental parsing branches and test malformed, skipped, and retried messages separately. Otherwise a syntactically correct continue may create redelivery storms or silent loss. Control-flow review must include these external effects, because the compiler only understands the lexical destination.
My E0696 checklist
- Does the target label belong to a block, loop, while, or for expression?
- Is repetition actually intended, or should the block break with a result?
- Which unit of work should the next iteration represent?
- What code after continue is skipped, including cleanup and accounting?
- Does retry logic still apply delay, cancellation, and a finite budget?
- Would a typed transition enum be clearer than several labels?
- Do semantic label names survive added nesting?
- Can tests distinguish continuing each possible loop?
The core principle is that continue belongs to repetition, not merely to labels. E0696 makes me identify the actual iterative operation. I introduce a loop only when the domain needs another iteration and then prove its progress and termination behaviour.