User defined type and foreach

Jonathan M Davis newsgroup.d at jmdavisprog.com
Fri Nov 17 09:50:59 UTC 2017


On Friday, November 17, 2017 07:40:35 Mike Parker via Digitalmars-d-learn 
wrote:
> On Friday, 17 November 2017 at 03:15:12 UTC, Tony wrote:
> > Thanks T! Good information, especially "iterating over a range
> > is supposed to consume it". I have been reading
> > dlang.org->Documentation->Language Reference, but  should have
> > also read dlang.org->Dlang-Tour->Ranges. Although that page
>
> You might also find use in this article (poorly adapted from
> Chapter 6 of Learning D by the publisher, but still readable):
>
> https://www.packtpub.com/books/content/understanding-ranges
>
> > makes a distinction about "range consumption" with regard to a
> > "reference type" or a "value type" and it isn't clear to me why
> > there would be a difference.
>
> With a value type, you're consuming a copy of the original range,
> so you can reuse it after. With a reference type, you're
> consuming the original range and therefore can't reuse it.

Technically, per the range API, you can _never_ reuse a range. The only
legitimate way to get a copy of a range to then iterate over separately is
to call save on the range (which of course requires it to then be a forward
range and not just an input range). However, unfortunately, for many common
range types (dynamic arrays included), calling save and copying the range
have the same semantics. So, it's easy to write code that will work with
many ranges without calling save anywhere but falls flat on its face as soon
as you use a range that actually requires that save be called (typically
because it's either a reference type, or it's a pseudo-reference type where
only some state gets copied when the range is copied, and you get
particularly weird behaviors when reusing the range that was copied).

So, while you can get away with reusing a range where save and copying the
range do the same thing, it's an incredibly bad idea in general and
definitely causes problems in generic code. Certainly, it should only be
done when you're dealing with a specific range type where you know what the
semantics of copying it are. In general, as soon as you've copied a range,
it should never be used again unless it's assigned a new value.

Of course, if something is truly an input range (and not a forward range
that merely doesn't have save declared like it should), then it's
particularly bad to be trying to use copies of ranges, because any range
that can't be a forward range is by definition either a reference type or a
pseudo-reference type where a copy is not fully distinct from the original
(typically where making a copy isn't possible or where it would be too
expensive to do so).

Personally, I'm inclined to think that we should never have had save and
should have required that reference type ranges which are forward ranges be
wrapped in a struct where copying it does the same thing that save does now,
but I seriously doubt that we could make a change that big now. And we'd
still have to watch out for how input ranges are different, since copying
them wouldn't and couldn't work the same (and while getting rid of save like
that would really clean up some range stuff that uses forward ranges, it
would make it a lot harder to distinguish between input and forward ranges).
So, it's not like there would be a perfect solution even if we were
redesigning things from scratch.

Ultimately, folks just need to be aware that they need to be calling save
when they want to actually copy a range and make sure that they unit test
their code well to make sure that it works with various range types if it's
generic code (and that it works with the exact ranges that it uses if it's
not generic). Unfortunately, it's usually the case that when you first test
range-based code with a range that doesn't implicitly save when it's copied
that you find that your code doesn't work with it, because save wasn't
explicitly called when it needed to be. But at least you then catch it and
can fix your code.

- Jonathan M Davis



More information about the Digitalmars-d-learn mailing list