Article: Why Const Sucks

Jonathan M Davis newsgroup.d at jmdavisprog.com
Tue Mar 6 06:49:38 UTC 2018


On Monday, March 05, 2018 22:21:47 Nick Sabalausky  via Digitalmars-d-
announce wrote:
> On 03/05/2018 12:38 PM, H. S. Teoh wrote:
> > This broke the by-value
> > assumption inherent in much of Phobos code,
>
> Wait, seriously? Phobos frequently passes ranges by value? I sincerely
> hope that's only true for class-based ranges and forward-ranges (and
> more specifically, only forward ranges where copying the range and
> calling .save are designed to do the exact same thing). Otherwise,
> that's really, *REALLY* bad since non-forward ranges *by definition*
> cannot be duplicated.
>
> Honestly, I think this is the one big flaw in the otherwise really nice
> design of ranges.
>
> The definition of "what is a forward/non-forward range" for struct-based
> ranges should have been "is this() @disabled (non-forward range), or is
> this() enabled *and* does the same thing as .save (forward range)?"
>
> Without that, this is a serious hole in non-forward ranges.

Passing ranges around by value is fine so long as you don't use the original
after the copy is made. Where you get screwed is when you then use the
original after the copy has been made. Almost nothing in Phobos passed
ranges around by ref, and doing so would actually make it a royal pain to
iteract with forward ranges, because save obviously isn't an lvalue, and
range-based functions that return new ranges aren't returning lvalues. So,
if range-based functions took their arguments by ref, then you couldn't
chain them. And using auto ref wouldn't fix the problem, since you could
still pass by value. It would just introduce all kinds of inconsistent
behavior as to whether a range was copied or not depending on how exactly a
range-based function were called, causing more bugs.

Honestly, I think that the correct way to implement forward ranges would
have been to disallowing ranges that weren't dynamic arrays or structs and
then use postblit constructors instead of save (classes could then be used
as ranges by wrapping them in structs, though even then, it would be better
to avoid classes as ranges, because all of those calls to new get to be
inefficent). With that, you wouldn't have all of these problems with
accidentally saving or not. Any time a forward range was copied, it would be
saved automatically, unless it could be moved, in which case, saving wasn't
necessary.

Unfortunately, that still leaves the problem of basic input ranges, since
they wouldn't have postblit constructors, and they could still be defined as
pseudo-reference types. Maybe we could require them to be defined as classes
to force them to be full-on reference types (they obviously can't be value
types, or they could be forward ranges), but then that would force
allocations for basic input ranges. _Most_ ranges can be at least forward
ranges, but some stuff can't reasonably be, and you wouldn't want to have to
allocate all of those on the heap. So, I don't have a clean solution for how
to deal with basic input ranges and copying, though I haven't sat down
recently and tried to work through the problem. In principle though, they're
reference types and ideally would be treated as such.

Regardless, I doubt that the design of ranges is going to be changed at this
point given the amount of code that would break as a result, and these sort
of changes are not backwards compatible.

- Jonathan M Davis



More information about the Digitalmars-d-announce mailing list