I would like to marry for-loops with generators in such a way that this would be possible. If you consider a Generator trait with 3 associated types: Yield, Resume and Return, then an Iterator<item=T> is just a Generator<Yield=T, Resume=(), Return=()>. We could make Generator the for-loop lang item instead of Iterator and extend for-loop syntax to allow it to handle these extra type parameters.
For Resume, we’d just allow continue to take an argument and have “continue” with no argument just be sugar for “continue ()”. A continue statement at the end of a for-loop block would be mandatory if the generator needs to resume with anything other than (). To demonstrate:
// These three are equivalent
for x in my_iter {
}
for x in my_iter {
continue;
}
for x in my_iter {
continue ();
}
// But we can also have iterators/generators where next/resume takes an argument.
// Here, continue is necessary.
for x in my_generator {
continue 123;
}
For Return, make the return value of the entire for expression be the return value of the generator. All iterators currently return () when they complete, but we could make RangeFrom a generator which returns !. So, in general, we’d allow code that does stuff like this:
let my_generator: impl Generator<Yield=T, Resume=(), Return=String> = ...;
let y: String = for x in my_generator {
...
}
Also, for the hell of it, allow for-loops to end in a then clause which can map the generator’s return value.
let my_generator: impl Generator<Yield=u32, Resume=(), Return=String> = ...;
let y: u32 = for x in my_generator {
if x < 123 {
break x;
}
} then z {
u32::parse(z).unwrap()
}
// `z` can be omitted if it has type `()`
let my_generator: impl Generator<Yield=u32, Resume=(), Return=()> = ...;
let y: u32 = for x in my_generator {
if x < 123 {
break x;
}
} then {
456
}
As a final touch, treat the argument to for as being mutably borrowed inside the main body of the for-loop, but as having been moved after the for-loop or within the then clause. Combined with non-lexical-lifetimes this would allow you to break or return with the generator before it’s finished being consumed. eg. this would be legal:
fn take_three<G: Generator<Yield=u32, Resume=(), Return=!>>(g: G) -> (Vec<u32>, Option<G>) {
let mut ret = Vec::new();
for x in g {
ret.push(x);
if ret.len() == 3 {
return (ret, Some(g));
}
}
(ret, None)
}
This would provide a way to use generators without any of those nasty runtime assertions which panic if we call .resume() on a generator which has already returned.
Edit: I actually have an RFC baking for this but it’s part of a much much larger effects-system RFC which I don’t know if I’ll ever get round to finishing.