DMD Bug or not? foreach over struct range

Nick Sabalausky SeeWebsiteToContactMe at semitwist.com
Wed May 16 13:50:17 PDT 2012


"Jonathan M Davis" <jmdavisProg at gmx.com> wrote in message 
news:mailman.828.1337154007.24740.digitalmars-d-learn at puremagic.com...
> On Wednesday, May 16, 2012 03:29:23 Nick Sabalausky wrote:
>> "Jonathan M Davis" <jmdavisProg at gmx.com> wrote in message
>> news:mailman.826.1337151940.24740.digitalmars-d-learn at puremagic.com...
>>
>> > No. That's expected. Your range is a value type, so it got copied when 
>> > you
>> > used it with foreach.
>>
>> But foreach isn't a function, it's a flow-control statement.
>
> If it _wasn't_ copied, using foreach would consume your range.

Iterating a range normally *does* consume it. And that's expected, as it 
should be: Imagine, for example, an input range that read from stdin. 
Leaving that range unconsumed would make no sense at all. Actually any input 
range can be expected to *not* leave an uniterated copy: if it *could* have 
an uniterated copy left behind, it would be a forward range, not an input 
range.

> It doesn't, and it would really suck if it did.

Like I said above, it would suck if you use the current foreach over an 
input range: Sometimes it might work by pure luck (as in the original 
example), but you can expect that for some input ranges it would fail 
miserably, because an input range is *not* a forward range and by definition 
cannot reliably save a previous state.

Additionally, if you wanted to still have an un-iterated version (of an 
actual foreard range, or an input range that you knew to be safe), then 
that's trivial:

Foo a;
b = a.save();  // Or "b = a;" if you really know what you're doing.
foreach(elem; a) {}
assert(a.empty);
assert(!b.empty);

Note, however, that there is no such simple way to go the other way around: 
to have the current "foreach over a copy" behavior and have access to the 
actual iterated range. Maybe if we had "foreach(e; ref range)", but AFAIK we 
don't.

Maybe it's just me, but I'd intuitively expect foreach over a range to work 
like this:

Range range;
foreach(e; range) {...}

-->

Range range;
for(; !range.empty(); range.popFront())
{
    auto e = range.front;
    ...
}

I was initially open to the idea I may have just misunderstood something, 
but I'm becoming more and more convinced that the current "foreach over an 
implicit copy" behavior is indeed a bad idea.

I'd love to see Andrei weigh in on this. I'm curious if the example you 
pointed out in TDPL made the copy deliberately, or if the effects of that 
copy were just an oversight.




More information about the Digitalmars-d-learn mailing list