foreach() behavior on ranges

Alexandru Ermicioi alexandru.ermicioi at gmail.com
Tue Aug 24 19:06:44 UTC 2021


On Tuesday, 24 August 2021 at 09:15:23 UTC, bauss wrote:
>
> A range should be a struct always and thus its state is copied 
> when the foreach loop is created.

Actually the range contracts don't mention that it needs to be a 
by value type. It can also be a reference type, i.e. a class.

>
> Which means the state resets every time the loop is initiated.

True for any forward range and above, not true for input ranges. 
The problem with them is that some of them are structs, and even 
if they are not forward ranges they do have this behavior due to 
implicit copy on assignment, which can potentially make the code 
confusing.

> If your range uses some internal state that isn't able to be 
> copied then or your ranges are not structs then your ranges are 
> inherently incorrect.

If we follow the definition of ranges, they must not be copy-able 
at all. The only way to copy/save, would be to have .save method 
and call that method. This again is not being properly followed 
by even phobos implementations.

Note, that a better approach would be to replace .save in 
definition of forward range with a copy constructor, then all 
non-compliant ranges would become suddenly compliant, while those 
that have .save method should be refactored to a copy constructor 
version.

>
> This is what a foreach loop on a range actually compiles to:
>
> ```d
> for (auto copy = range; !copy.empty; copy.popFront())
> {
>     ...
> }
> ```

You should add .save on assignment if range is a forward range, 
or just remove the assignment if it is not.

Best regards,
Alexandru.




More information about the Digitalmars-d-learn mailing list