RFA-419 · Case file with fixtures · Case 391 of 694 · Compiler evidence
break with a Value Is Not Allowed in a while Loop
Only loop expressions and labeled breakable blocks can receive a value from break. while and for support valueless break for control flow; switch to loop when the construct must compute a result.
- 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
- Only loop expressions and labeled breakable blocks can produce a value through break; while and for loops use break solely as a control exit without a payload.
- First discriminating check
- Use loop when break should construct the expression result, or assign outside and use a valueless break for conditional iteration.
I wrote while true and tried to break with the value produced by the loop. Rust rejected it with E0571. A condition that is always true does not turn a while expression into the loop construct.
The failing fixture contains break 7 inside while true. The compiler says values can be broken only from loop or a breakable block.
loop can be a value-producing expression
Rust's loop expression rules allow a break expression to carry the result of loop:
let value = loop {
if ready() {
break 7;
}
};
The type of the loop expression is determined by the values supplied by its reachable breaks. The repaired fixture uses this form and asserts that the result is seven.
This is useful for retry-until-ready, parser search, and initialization that requires several attempts.
while has a condition-driven exit
A while loop can end because its condition becomes false. That exit has no break value expression from which to derive a result.
Rust gives while the unit result type and allows only valueless break for an early exit. The fact that a particular condition is written as literal true does not change the language category or typing rule.
The compiler also warns that an infinite while true should normally be written as loop, making intention explicit.
for has the same valueless break rule
A for loop ends when its iterator returns None. Like while, it does not accept break value.
If I need to find a value, iterator operations often state the goal better:
let value = items.find(|item| matches(item));
If the search has complex mutable state or several exit outcomes, a surrounding loop can own the result while a for loop is nested inside, or I can assign an Option and break without a payload.
I choose the structure that makes exhaustion policy visible.
Assignment plus valueless break is an alternative
Existing while logic can keep its form by storing the result outside:
let mut found = None;
while condition() {
if let Some(value) = attempt() {
found = Some(value);
break;
}
}
This is appropriate when condition-false completion and early-found completion both need representation. The outer Option records whether a value was produced.
Switching mechanically to loop could accidentally remove a meaningful condition-false exit, so I understand the state machine before applying the compiler's suggested form.
All valued breaks need compatible types
Inside a value-producing loop, every reachable break expression contributing to that loop must agree on the loop's result type.
A branch breaking with an integer and another with a String causes a separate type mismatch. Valueless break corresponds to unit and also conflicts with a non-unit loop result unless control-flow structure prevents it from targeting the same expression.
I define a domain enum when exits have different meanings instead of forcing unrelated data into one tuple or sentinel.
Labels select which construct receives the break
Nested loops can use labels:
let result = 'search: loop {
for item in next_batch() {
if accepted(item) {
break 'search item;
}
}
};
The valued break targets the labeled loop, not the inner for. This keeps batch exhaustion and final search success separate.
Labels are also available on breakable blocks under the Reference rules. I use them sparingly and name the target by purpose.
E0571 catches a semantic ambiguity
The E0571 explanation is short, but the design question is important: what result should condition-false or iterator-exhausted completion produce?
Rust does not invent a default for while or for. The programmer chooses Option, Result, a preinitialized variable, or a loop whose only normal exits carry values.
This removes hidden sentinel behavior from the language construct.
Divergence still matters
A loop with no reachable break has type ! and can coerce where other types are expected because it never returns normally. Adding a valued break makes it a result-producing expression.
Panics and returns inside the body leave a different enclosing control flow and do not contribute a normal loop result. I test the exits, not only the happy value.
My refactoring questions
When E0571 appears, I ask:
- Is the condition-false exit meaningful?
- What value represents exhaustion?
- Should the result be Option or Result?
- Can an iterator search express the operation?
- Which nested loop should receive the break?
- Do all normal exits carry one compatible type?
The core principle is that syntax expresses the source of completion. while and for can finish without a break payload, so they do not produce one. loop has no condition-driven normal exit and can use valued breaks to construct its result. Choosing between them makes the state machine explicit.