On Tuesday, 24 August 2021 at 09:26:20 UTC, jfondren wrote:
I think you strayed from the beaten path, in a second way, as soon as your range's lifetime escaped a single expression, to be possibly used in two foreach loops. With ranges, as you do more unusual things, you're already encouraged to use a more advanced range. And ranges already have caveats for surprising behavior, like map/filter interactions that redundantly execute code. So I see this as a documentation problem. The current behavior of 'if you break then the next foreach gets what you broke on' is probably a desirable behavior for some uses:
Yes, I have a special case where a delegate jumps back to the range because something must be buffered before it can be delivered.
```d import std; class MyIntRange { int[] _elements; size_t _offset; this(int[] elems) { _elements = elems; }bool empty() { return !_elements || _offset >= _elements.length; }int front() { return _elements[_offset]; } void popFront() { _offset++; } } void main() { auto ns = new MyIntRange([0, 1, 1, 2, 3, 4, 4, 4, 5]); // calls writeln() as many times as there are numbers: while (!ns.empty) { foreach (odd; ns) { if (odd % 2 == 0) break; writeln("odd: ", odd); } foreach (even; ns) { if (even % 2 != 0) break; writeln("even: ", even); } } } ```
That is just weird. It's not logical and a source of bugs. I mean, we should use foreach() to avoid loop-bugs. Then it's a desired behavior to rely on that?
