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