how is this array subtyping inside struct (bug?) possible?
schveiguy at gmail.com
Mon Aug 10 19:52:19 UTC 2020
On 8/10/20 3:39 PM, mw wrote:
> On Monday, 10 August 2020 at 19:30:18 UTC, Steven Schveighoffer wrote:
>> On 8/10/20 2:36 PM, mw wrote:
>>> On Monday, 10 August 2020 at 18:29:56 UTC, mw wrote:
>>>> and where in this doc on range:
>>>> it is mentioned that: the length property of the range will be
>>>> changed after the range is "consumed" by foreach loop?
>>>> foreach (element; range)
>>>> // Loop body...
>>>> it's internally rewritten similar to the following:
>>>> for (auto __rangeCopy = range;
>>>> auto element = __rangeCopy.front;
>>>> // Loop body...
>>> And wait, ... did I saw "__rangeCopy" here? it should be a *copy*?!
>> In your code, the type of "range" is SharedArray!(string) which is a
>> class or *reference type*.
>> So let's follow along what happens:
>> SharedArray!(string) fns;
>> for(auto __rangeCopy = fns;
>> // the above makes a copy of a *class reference*, which means it does
>> not make a copy of the *array data* or even the array reference. It's
>> like copying a pointer to the array.
>> // SharedArray(T) does not have empty member, so this forwards to the
>> array, it's like saying:
>> // !__rangeCopy.array.empty
>> // This is equivalent to __rangeCopy.array.popFront, which alters the
>> array inside the ONE SHARED class instance.
> This still doesn't explain why the underlying array.length is modified
> after the range to consumed; too much black magic is happening here.
Indeed there is a lot of magic. It's ordinary every-day D magic though ;)
string arr = ["abc"];
arr.popFront; // ufcs function defined in std.range.primitives
assert(arr.length == 0); // reduces the length
If we inlined this fully with the definition of
std.range.primitives.popFront, the line:
is really doing:
__rangeCopy.array = __rangeCopy.array[1 .. $];
This is how you iterate an array as a range.
>> How to fix? extract the array before iterating:
> This defeats the purpose, i.e. the convenience that subtyping mechanism
> supposed to provide.
You are subtyping but inadvertently have turned a forward range (array)
into an input range (iterate only once) by changing it into a class.
forward ranges aren't supposed to technically be valid if you don't
*save* them, but in practice it's totally fine for arrays.
What might work (and I haven't tried this) is to @disable front,
popFront, empty, and I think what will then happen is the compiler will
try slicing instead (which should work) and iterate a copy of the array.
alias array this;
More information about the Digitalmars-d