foreach() behavior on ranges

Steven Schveighoffer schveiguy at gmail.com
Tue Aug 24 13:02:38 UTC 2021


On 8/24/21 4:36 AM, 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().
> 
> For some reason, foreach() does not call popFront() on a break or 
> continue statement. 

continue calls `popFront`. break does not.

> There is no way to detect it except the range itself 
> tracks its status and does an implicit popFront() if needed - but then 
> this whole interface is some kind of useless.

You can call `popFront` if you need to after the loop, or just before 
the break. I have to say, the term "useless" does not even come close to 
describing ranges using foreach in my experience.

> There is opApply() on the other hand that is designed for foreach() and 
> informs via non-0-result if the loop is cancelled - but this means that 
> every range must implement it if the range should work in foreach() 
> correctly?

`opApply` has to return different values because it needs you to pass 
through its instructions to the compiler-generated code. The compiler 
has written the delegate to return the message, and so you need to pass 
through that information. The non-zero result is significant, not just 
non-zero. For instance, if you end with a `break somelabel;` statement, 
it has to know which label to go to.

The correct behavior for `opApply` should be, if the delegate returns 
non-zero, return that value immediately. It should not be doing anything 
else. Would you be happy with a `break somelabel;` actually triggering 
output? What if it just continued the loop instead? You don't get to 
decide what happens at that point, you are acting as the compiler.

> This is very inconsistent. Either foreach() should deny usage of ranges 
> that have no opApply() method or there should be a reset() or cancel() 
> method in the interfaces that may be called by foreach() if they are 
> implemented.
> 
> How do you handle that issue? Are your ranges designed to have this bug 
> or do you implement opApply() always?

It's not a bug. So there is no need to "handle" it.

The pattern of using a for(each) loop to align certain things occurs all 
the time in code. Imagine a loop that is looking for a certain line in a 
file, and breaks when the line is there. Would you really want the 
compiler to unhelpfully throw away that line for you?

And if that is what you want, put `popFront` in the loop before you 
exit. You can't "unpopFront" something, so this provides the most 
flexibility.

-Steve


More information about the Digitalmars-d-learn mailing list