Range Redesign: Copy Semantics

Jonathan M Davis newsgroup.d at jmdavisprog.com
Sun Jan 21 13:48:56 UTC 2024


On Sunday, January 21, 2024 6:22:26 AM MST Alexandru Ermicioi via Digitalmars-
d wrote:
> For all these cases there is `.save` method which seems to be
> ignored in most of the code and compiler, when it should not be.
> It explicitly defines that range is copied irrelevant of the type
> of range you've mntioned. Since `.save` is defined on forward
> range, this means that pure input range must never be copyable,
> i.e. copy and post constructors must be disabled.
>
> If this rule is followed, issues with copying would dissappear.
>
> Even more drastic measure would be to disable copying on all
> types of ranges, and allow copying only through `.save` method,
> i.e. do explicit copying when it is intended to.

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. And you also can't disable
copying on classes or dynamic arrays. We could disallow classes which are
ranges (even for basic input ranges) if we really wanted to make it so that
they couldn't be copied, but we couldn't do the same with dynamic arrays.
And really, ranges need to be copyable, because we need to be able to pass
them around around and store them. And while ref could theoretically be used
in some of those cases, in many of them, it could not (e.g. you have to copy
a range once you wrap it in another type, and the ability to do that is
crucial for most range-based algorithms).

Also, forward ranges are supposed to mimic dynamic arrays. C++ iterators are
meant to mimic pointers, and D's ranges are meant to mimic dynamic arrays.
The only reason that we even have save is to support ranges which are
classes. A lot of the problems with range-based code come from code that
assumes that a range behaves like a dynamic array when it doesn't. If we
required that all forward ranges have the same copy semantics as dynamic
arrays, then that problem is solved. Having save means treating forward
ranges differently than we treat dynamic arrays, and it's completely
unnecessary so long as we're willing to require that classes be wrapped in
structs to be forward ranges. The ideal situation here is for ranges in
general to have the same semantics as dynamic arrays with regards to
copying, assignment, and iteration.

So, we can't disable copying for ranges in general and have them work, and
even if we all agreed that disabling copying for ranges in general was a
good idea, we still couldn't do that for dynamic arrays. So, we'd continue
to have the problem that code would be written to be range-based but only
worked with arrays, because that was what it was tested with. By requiring
that forward ranges have the same semantics as dynamic arrays and treating
basic input ranges as a separate thing, it becomes possible to write code
that assumes the same semantics as dynamic arrays and then works with all
forward ranges, which would significantly reduce the number of bugs that we
get in range-based code.

- Jonathan M Davis





More information about the Digitalmars-d mailing list