std.range's put on arrays needs to change

Steven Schveighoffer schveiguy at gmail.com
Sun Jun 26 12:57:15 UTC 2022


On 6/25/22 7:44 PM, WebFreak001 wrote:
> let's see this code:
> 
> ```d
> import std.range;
> 
> int[] arr;
> arr.put(2);
> ```
> 
> someone not coming from a D backround (or even those coming from it) 
> might expect here to have `arr` be equal to `[2]` afterwards.
> 
> However this is not the case:
> 
> ```
> core.exception.AssertError@/dlang/dmd/linux/bin64/../../src/phobos/std/range/primitives.d(2472): 
> Attempting to fetch the front of an empty array of int
> ----------------
> ??:? _d_assert_msg [0x563cbe9c3360]
> /dlang/dmd/linux/bin64/../../src/phobos/std/range/primitives.d:2472 pure 
> nothrow ref @property @nogc @safe inout(int) 
> std.range.primitives.front!(int).front(return scope inout(int)[]) 
> [0x563cbe9a0fe3]
> /dlang/dmd/linux/bin64/../../src/phobos/std/range/primitives.d:297 pure 
> nothrow @nogc @safe void std.range.primitives.doPut!(int[], 
> int).doPut(ref int[], ref int) [0x563cbe9a0f62]
> /dlang/dmd/linux/bin64/../../src/phobos/std/range/primitives.d:380 pure 
> nothrow @nogc @safe void std.range.primitives.put!(int[], int).put(ref 
> int[], int) [0x563cbe9a0f37]
> ./onlineapp.d:8 _Dmain [0x563cbe9a0ee1]
> ```
> 
> this is because `put` is there to write into a pre-allocated buffer if 
> you pass in an array, as [it assumes it is a 
> slice](https://dlang.org/phobos/std_range_primitives.html#put):
> 
> ```d
> int[3] buffer;
> auto slice = buffer[];
> put(slice, 1);
> put(slice, 2);
> put(slice, 3);
> assert(!slice.length);
> assert(buffer == [1,2,3]);
> ```
> 
> which seems to be the way this is intended to be used.
> 
> However now I argue this is very unintuitive behavior, especially when 
> you compare it to appender!int:
> 
> ```d
> auto arr = appender!(int[]);
> arr.put(1);
> assert(arr.data == [1]);
> ```
> 
> put on an appender actually adds to the array, the appender allocates 
> memory for it.
> 
> I strongly feel that this behavior is very inconsistent and unexpected 
> for most users and should be changed. I think we should deprecate simple 
> arrays/slices on `put` and rename the method for those.
> 
> I propose put with slices should be renamed to `putInto`.

It might not be expected, but to disable allowing reference input ranges 
as output ranges would break so many fundamental usages, such as 
`std.algorithm.copy`. You wouldn't just be disabling slices, it would be 
a complete removal of a whole class of output ranges.

What we need is an appendTo wrapper:

```d
struct AppendTo(T)
{
    private T[] *arr;
    this(return T[]* arr) { this.arr = arr; }
    void put(X)(X val) if (__traits(compiles, (*arr) ~= val)) {
       (*arr) ~= val;
    }
}

auto appendTo(T)(return ref T[] arr)
{
    return AppendTo!T(&arr);
}
```

-Steve


More information about the Digitalmars-d mailing list