Coming from https://github.com/rust-lang/rfcs/issues/1767#issuecomment-583691737 with my proposal dumped below. I think the dispute over whether or not the loop should return an Option is completely unnecessary given the proper desugaring rule.
I would really love to have this feature, so I try to make my proposal:
- be useful in everyday situations
- have a clear and intuitive formal semantics and typing rule
- avoid the semantical confusion of
else
Long story short, the while
loop is followed by a then
clause:
while BOOL {BLOCK1} then {BLOCK2}
This should be desugared to, and therefore have the same type and semantics with:
loop {if (BOOL) {BLOCK1} else {break {BLOCK2}}}
just as the usual while loop
while BOOL {BLOCK1} // then {}
have already and always been desugared to
loop {if (BOOL) {BLOCK1} else {break {}}}
It requires a bit more care for for
but the story remains basically the same.
Note that the break
in the then
clause is harmless but redundant (just as the last expression in a function prefixed with return
), since it will be desugared to break (break ...)
.
The choice of then
over else
or final
is explained in #961
I would suggest then
instead of final
, since in all currently popular languages where it exists, final(ly)
means the exact opposite of getting executed only when not being break-ed before, which is getting executed whatsoever. then
would avoids the sort of naming tragedy like return
in the Haskell community.
then
also avoids the semantical confusion brought by else
, since it naturally has a sequential meaning (I eat, then I walk) in parallel with its role in the conditional combination (if/then). In places where it joints two blocks ({ ... } then { ... }
) instead of a boolean and a block (x<y then { ... }
), the sequential semantics prevails intuitively.
This syntax can be used wherever the loop is meant to find something instead of to do something. Without this feature, we usually do the finding and then put the result somewhere, which is a clumsy emulation of just to find something.
For example:
while l<=r {
let m = (l+r)/2;
if a[m] < v {
l = m+1
} else if a[m] > v {
r = m-1
} else {
break Some(m)
}
} then {
println!("Not found");
None
}
which means:
loop {
if (l<=r) {
let m = (l+r)/2;
if a[m] < v {
l = m+1
} else if a[m] > v {
r = m-1
} else {
break Some(m)
}
} else {
break {
println!("Not found");
None
}
}
}
Even this desugared version is cleaner than something like
{
let mut result = None;
while l<=r {
let m = (l+r)/2;
if a[m]<v {
l = m+1
} else if a[m]>v {
r = m-1
} else {
result = Some(m);
break
}
}
if result==None {
println!("Not found");
}
result
}