foreach() behavior on ranges

jfondren julian.fondren at gmail.com
Tue Aug 24 09:26:20 UTC 2021


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);
         }
     }
}
```


More information about the Digitalmars-d-learn mailing list