passing static arrays to each! with a ref param [Re: Why can't static arrays be sorted?]
Jon Degenhardt via Digitalmars-d-learn
digitalmars-d-learn at puremagic.com
Mon Oct 10 21:24:08 PDT 2016
On Monday, 10 October 2016 at 16:46:55 UTC, Jonathan M Davis
wrote:
> On Monday, October 10, 2016 16:29:41 TheGag96 via
> Digitalmars-d-learn wrote:
>> On Saturday, 8 October 2016 at 21:14:43 UTC, Jon Degenhardt
>> wrote:
>> > This distinction is a bit on the nuanced side. Is it
>> > behaving as it should?
>> >
>> > --Jon
>>
>> I think so? It's not being modified in the second case because
>> the array is being passed by value... "x" there is a reference
>> to an element of the copy created to be passed to each(). I
>> assume there's a good reason why ranges in general are passed
>> by value into these functions -- except in this one case, the
>> stuff inside range types copied when passed by value won't be
>> whole arrays, I'm guessing.
>
> Whether it's by value depends entirely on the type of the
> range. They're passed around, and copying them has whatever
> semantics it has. In most cases, it copies the state of the
> range but doesn't copy all of the elements (e.g. that's what
> happens with a dynamic array, since it gets sliced). But if a
> range is a class, then it's definitely a reference type. The
> only way to properly save the state of a range is to call save.
>
> But passing by ref would make no sense at all with input
> ranges. It would completely kill chaining them. Almost all
> range-based functions return rvalues.
>
> - Jonathan M Davis
The example I gave uses ref parameters. On the surface it would
seem reasonable to that passing a static array by ref would allow
it to be modified, without having to slice it first. The
documentation says:
// If the range supports it, the value can be mutated in place
arr.each!((ref n) => n++);
assert(arr == [1, 2, 3, 4, 5]);
but, 'arr' is a dynamic array, so technically it's not describing
a static array (the opApply case).
Expanding the example, using foreach with ref parameters will
modify the static array in place, without slicing it. I would
have expected each! with a ref parameter to behave the same.
At a minimum this could be better documented, but it may also be
a bug.
Example:
T increment(T)(ref T x) { return x++; }
void main()
{
import std.algorithm : each;
int[] dynamicArray = [1, 2, 3, 4, 5];
int[5] staticArray = [1, 2, 3, 4, 5];
dynamicArray.each!(x => x++); // Dynamic array by
value
assert(dynamicArray == [1, 2, 3, 4, 5]); // ==> Not modified
dynamicArray.each!((ref x) => x++); // Dynamic array by
ref
assert(dynamicArray == [2, 3, 4, 5, 6]); // ==> Modified
staticArray[].each!((ref x) => x++); // Slice of static
array, by ref
assert(staticArray == [2, 3, 4, 5, 6]); // ==> Modified
staticArray.each!((ref x) => x++); // Static array by
ref
assert(staticArray == [2, 3, 4, 5, 6]); // ==> Not Modified
/* Similar to above, using foreach and ref params. */
foreach (ref x; dynamicArray) x.increment;
assert(dynamicArray == [3, 4, 5, 6, 7]); // Dynamic array =>
Modified
foreach (ref x; staticArray[]) x.increment;
assert(staticArray == [3, 4, 5, 6, 7]); // Static array
slice => Modified
foreach (ref x; staticArray) x.increment;
assert(staticArray == [4, 5, 6, 7, 8]); // Static array =>
Modified
}
More information about the Digitalmars-d-learn
mailing list