On Monday, 22 June 2020 at 21:33:08 UTC, H. S. Teoh wrote:
Don't be shocked when you find out how many Phobos ranges have
.init states that are invalid (e.g., non-empty, but .front and
.popFront will crash / return invalid values).
Which ones?
Jonathan is coming from the POV of generic code. The problem
with move leaving the original range in its .init state isn't
so much that it will crash or anything (even though as you said
that does indicate a flaw in the range's implementation), but
that the semantics of generic code changes in subtle ways.
Well that means that the code is not generic, i.e. the bug
originates in the design, not implementation.
auto myGenericFunc(R)(R r) {
...
foreach (x; r) {
doSomething(x);
}
if (!r.empty)
doSomethingElse(r);
...
}
Suppose for argument's sake that the above foreach/if structure
is an essential part of whatever algorithm myGenericFunc is
implementing. Now there's a problem, because if R has
array-like semantics, then the algorithm will do one thing, but
if R has reference-like or move semantics, then the behaviour
of the algorithm will be different, even if both ranges
represent the same sequence of input values.
Yep, definitely not generic. Which is exactly the kind of error
that should be caught at compile time. Which we, sadly, can't do
with the current range API.
consider a function that drops the first n elements of a range.
Your generic function might want to pop the first n elements
then do something else with the rest of the range. Well, if
you write it the obvious way:
auto myAlgo(R)(R r) {
size_t n = ...;
dropFirstN(r, n);
... // do something else with r
}
then you have a subtle bug, because the state of r after the
call to dropFirstN might be completely different depending on
whether r behaves like an array or like a by-reference or move
type.
Err... that one is actually fine. It would take the range by ref
and pop off the elements. There already is such a function - the
popFrontN. It's the functions that take ranges by value that
present the not-so-subtle issue with reference types. For
example, its chainable would-be-counterpart drop(). "Would-be"
because that one takes the argument by value.
We should move toward disallowing reference types to be ranges. A
good deal of the rest can be solved with API and design changes
(like disallowing copying of input ranges).
It's kind of interesting how, with the ongoing discussion about
range API in the general forum, a couple of range questions are
brought up in learn. Something, as they say, is in the air.