On Tuesday, 24 August 2021 at 08:36:18 UTC, frame wrote:
Consider a simple input range that can be iterated with
empty(), front() and popFront(). That is comfortable to use
with foreach() but what if the foreach loop will be cancelled?
If a range isn't depleted yet and continued it will supply the
same data twice on front() in the next use of foreach().
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:
```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);
}
}
}
```