RFA-468 · Case file with fixtures · Case 440 of 694 · Compiler evidence
Rust break and continue Labels Must Be Declared on an Enclosing Loop
Loop labels live in a dedicated lexical namespace and target enclosing control-flow constructs. Declare the exact label before the loop, fix a stale spelling, or simplify the flow when the target no longer exists.
- 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
- Labels are lexical control-flow declarations in their own namespace and cannot target a removed, misspelled, or non-enclosing construct.
- First discriminating check
- Locate the intended enclosing operation and decide whether control should break, continue, return, or propagate a result before declaring the exact label.
I wrote break 'outer inside a loop without declaring 'outer. Rust returned E0426 because the label cannot be resolved to an enclosing control-flow target.
The failing fixture demonstrates that labels are declarations with lexical scope, not arbitrary messages attached to break.
A label is declared before its target
The repaired fixture writes:
'outer: loop {
break 'outer;
}
The apostrophe makes a label look similar to a lifetime, but labels occupy a dedicated namespace. The namespace Reference lists labels separately from types, values, macros, and lifetimes.
The target must lexically enclose the break or continue that names it.
Labels disambiguate nested loops
An unlabeled break exits the innermost loop. With nested scanning, retry, or traversal loops, a label can intentionally exit an outer one:
'records: for record in records {
for field in record.fields() {
if invalid(field) {
break 'records;
}
}
}
The loop-label Reference defines these targets. I name labels after the operation being controlled, not only outer and inner, when nesting is non-trivial.
A stale label often follows refactoring
E0426 commonly appears when an outer loop is extracted into a helper or converted to iterator logic while an inner break 'search remains.
I inspect the intended control-flow outcome before re-adding the old label. The helper may now need to return, return an enum, or propagate a ControlFlow value instead of reaching across a function boundary.
Labels cannot target a loop in another function because lexical scope and stack control would be unclear.
continue requires a loop target
continue 'name skips to the next iteration of the named enclosing loop. A labelled block is not an iteration source, so continue has stricter target needs than value-bearing break.
I verify whether the operation means “finish this search,” “skip this record,” or “retry this inner attempt.” Similar-looking label fixes can change which work executes next.
Tests should include data after the control-flow event so a wrong target becomes visible.
Labelled blocks can return early from a region
Rust also supports labelled block expressions with break 'label value. This can replace a small immediately-invoked closure or deeply nested conditional when a local early result is useful.
I use it sparingly and name the block by its result. The label still must be declared on the enclosing block, and ordinary continue does not apply.
If a normal helper function expresses the boundary better, I prefer that.
Labels and lifetimes share spelling but not meaning
'outer in break 'outer is resolved in the label namespace. 'a in &'a T is a lifetime parameter. Their apostrophe syntax does not make them interchangeable.
A lifetime declaration on a function does not satisfy E0426, and a loop label cannot be used as a reference lifetime. Reading the syntactic position is essential.
The E0426 explanation focuses on spelling and declaration, but namespace context explains why a similarly named lifetime does not help.
Macros should keep declaration and use together
A macro that emits only break 'outer assumes a label at its expansion site. That makes the macro fragile and can fail under a different caller structure.
I prefer passing a label deliberately when macro syntax supports it or generating the labelled control-flow region as one unit. Macro documentation should state any required surrounding target.
My E0426 checklist
For asynchronous code, I remember that a label only changes synchronous control flow inside the current future. It does not cancel already spawned tasks or undo effects performed in earlier iterations. When a labelled break ends a coordination loop, I review cleanup, channel closure, and task ownership explicitly. A correct label target can still leave background work running. The compiler verifies lexical control-flow validity; the surrounding shutdown protocol remains an engineering responsibility.
- Is the label declared with the exact spelling?
- Does its loop or block lexically enclose this statement?
- Should the operation break, continue, or return?
- Did refactoring remove or move the original target?
- Which nested work should execute after control transfers?
- Am I confusing a lifetime name with a label?
- Is a macro assuming a caller-owned label?
- Would a helper result or
ControlFlowmake the boundary clearer?
The core principle is that labelled control flow remains explicit and lexical. I resolve each jump to a visible enclosing construct and review the intended next operation, rather than restoring a name without checking what the refactored code should now do.