Non-local control flow is a subclass of functions with multiple return points. As also already observed, explicit breaks to outer loops are a very similar feature and implicitely requires that code is structured in nested scopes, and does not permit arbitrary goto
. Maybe we could unify them under the break
keyword? It doesn't need to be a special feature of closures but it would require expanding the notion of function types. However, to make fold
work would require reworking it which may be difficult in a compatible manner. An additional version would pose no problem though.
// A function with two return points. As with reference parameter,
// requires the caller to provide that the lifetime lives for the full
// function call.
fn mul_early<'early>(acc: i32, num: i32) -> i32, 'early: i32 {
if num == 0 {
break 'early 0;
}
return acc * num;
}
trait BreakFold {
type Item;
/// Fold over a function that may return the final value early.
fn fold<A, F>(&mut self, init: A, f: F) -> A where
F: (FnMut<'br>(A, Self::Item) -> A, 'br: A);
}
impl<I: Iterator> ManualFold for I {
type Item = <I as Iterator>::Item;
fn fold<A, F>(&mut self, init: A, mut f: F) -> A where
F: (FnMut<'br>(A, Self::Item) -> A, 'br: A),
{
// The early return provides a value for this block directly.
'early: {
let mut acc = init;
for item in self {
acc = f::<'early>(acc, item);
}
acc
}
}
}
This explicit syntax choice would address problems named in the earlier thread:
- The special meaning compared to normal return is immediately clear, in particular for compiler internals it becomes obvious that the tear-down of the function frame needs to be special cased and potentially performed in a manual way.
- It's explicit and requires the caller of such a closure/function to explicitely opt-in.
- It introduces an name for the co-variables that model the code flow.