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
}