Range Redesign: Copy Semantics

Jonathan M Davis newsgroup.d at jmdavisprog.com
Mon Jan 22 03:06:24 UTC 2024


On Sunday, January 21, 2024 11:13:02 AM MST Nick Treleaven via Digitalmars-d 
wrote:
> On Sunday, 21 January 2024 at 13:48:56 UTC, Jonathan M Davis
>
> wrote:
> > And how on earth would you use a range if it can't be copied?
> > They're copied all over the place and have to be to even be
> > used. You copy them when you pass them to any range-based
> > function. You copy them when you return them from a range-based
> > function. You copy them when they get wrapped by another range.
> > You copy them when you pass them to foreach. Disabling copying
> > on ranges would make them borderline useless.
>
> Maybe they could be moved instead of copied when the original is
> not needed.

Do you really want to have to call move every time that you call a
range-based function or want to wrap one range in another? Immovable types
are incredibly un-user-friendly in general.

It also would mean that ranges wouldn't work with foreach with how foreach
currently works, since it copies the range. And if it doesn't copy the
range, then ranges other than dynamic arrays would have different semantics
when being iterated with foreach than dynamic arrays do, which would also be
a big problem.

> > And you also can't disable copying on classes or dynamic arrays.
>
> Unless all ranges have to be structs, but then we probably need a
> function/syntax to ask for a range from a class or dynamic array.

Forward ranges are supposed to mimic dynamic arrays. Having to wrap dynamic
arrays to use them as ranges would be very annoying. And for what? To just
avoid copying? Yes, if copying ranges is illegal, then you've given them all
the same copy semantics, but the result is incredibly unwieldy, and it means
that you can't have dynamic arrays and forward ranges use the same code
without wrapping dynamic arrays. And are people really going to want to be
wrapping dynamic arrays in another type just so that they can make them
uncopyable and the be forced to call move all over the place instead?

The most friendly way for forward ranges to function is to make them more
properly emulate dynamic arrays as they were originally intended to do. The
reason that they don't already is so that we could support classes as
ranges, which while it should work, is not the typical use case, and we can
allow them to be forward ranges almost as easily with a wrapper struct as we
can by having a save function.

If forward ranges all have the same copy semantics as dynamic arrays, then
they become easy to use correctly without having bugs due to stuff like
forgetting to call save - and without a bunch of compiler errors, because
you forgot to use move all over the place. For forward ranges, it would be a
huge win, and it's exactly what we've talked about doing for years now. We
just haven't done it, because doing it in the current version of Phobos
would break existing code.

The bigger problem is then what to do with basic input ranges, because they
can't have the same copy semantics, but the fact that they can't implement
something like save already hamstrings them enough that arguably they should
be treated as something separate anyway. So, by just giving them a separate
API, we simplify the situation with forward ranges considerably, and we
avoid the classes of bugs that come with code being written to work with
both basic input ranges and forward ranges. Of course, there are some cases
where having code written to work with both works just fine, and having
separate APIs could be annoying in those cases, but basic input ranges are
limited enough that most code doesn't support them anyway.

Obviously, the tradeoffs are debatable, but I think that it's clear that
ideal situation for forward ranges is to have them all have the same copy
semantics as dynamic arrays.

- Jonathan M Davis





More information about the Digitalmars-d mailing list