August 09, 2012
I just saw that the target range passed to std.algorithm.copy is not
passed by reference. So for a range which is implemented as a simple
struct value type and which modifies some internal state a call to copy
does not have any effect.
It can be worked around by passing a pointer to that range instead of
the range itself, but is there any reason why we can't just add a 'ref'
to the target range parameter?
August 09, 2012
On 08/09/2012 06:32 AM, Johannes Pfau wrote:
> I just saw that the target range passed to std.algorithm.copy is not
> passed by reference. So for a range which is implemented as a simple
> struct value type and which modifies some internal state a call to copy
> does not have any effect.

I think this is related to the separation of the concepts of container and range.

> It can be worked around by passing a pointer to that range instead of
> the range itself, but is there any reason why we can't just add a 'ref'
> to the target range parameter?

That would have the problem of losing elements from a slice when used as an output range:

import std.stdio;
import std.range;

void main()
{
    int[] slice = [ 1, 2, 3 ];
    put(slice, 100);
    writeln(slice);
}

The output:

[2, 3]

The workaround is to make a copy of the output range:

import std.stdio;
import std.range;

void main()
{
    int[] slice = [ 1, 2, 3 ];
    int[] slice2 = slice;

    put(slice2, 100);

    writeln(slice2);
    writeln(slice);
}

The output:

[2, 3]
[100, 2, 3]    <-- original slice is preserved

This is happening as a result of decisions made to make output ranges very flexible. I try to explain this flexibility under the OutputRange section here:

  http://ddili.org/ders/d.en/ranges.html

That page lists the ways an object is considered to be used as an output range. Slices don't have a put() member function, so they end up being used as in the following:

  range.front = e;
  range.popFront();

Anyway, I think that's why copy() takes by copy. :)

Ali