If an iterator is not statically known to be infinite, but it is, we can wrap it in some adapter like unreachable_end(iter) which replaces the end of iterator with unreachable!(). But in your examples (0..).filter(|x| *x != 10) can implement InfiniteIterator and I don't see the reason why queue one can't.
About break with value, changing map to filter_map doesn't change anything, but for example .take will make the iterator finite. In this case I can see these options:
Chain with some iterator that immediately panics, or use unreachable_end(iter).
Wrap the for with a labeled block (or labeled loop) and change break labels to the outer block:
let foo = 'outer: {
for x in some_infinite_iterator.take(5) {
if some_condition(x) { break 'outer x; }
}
// this case were unreachable but now it is needed
some_default
};
Itās based on having InfiniteIterator as a subtrait of Iterator with a next_infinite method. It provides an ifor! macro as a superset of the forā¦in syntax, automatically supporting break-with-value for InfiniteIterators and normal break for regular Iterators (via inherent method specialization). I didnāt go with a blanket impl to allow the case of types like Map.
Nice generalization, though I'm not a fan of exhausted or else block. I prefer returning generator result directly. It would make examples in my original post work as is (without special casing ! type) and type system would understand that codes after infinite for loops are unreachable (which you mentioned it as a drawback? The section about returning generator values and ! type looks wrong to me, I think break num example should work since ! can be casted to any type). Some adapters for generators (as you mentioned) and iterators for converting them to generators can do the job of exhausted block.