On Thursday, 18 May 2017 at 18:27:22 UTC, Andrei Alexandrescu wrote:
Iterating over a container using e.g. foreach won't consume the
container same as iterating over int[] won't consume the slice.

I don't understand why you're mapping the behavior of ranges/slices, which theoretically are shrinking views of data, onto containers of data which can grow or shrink. They seem antithetical to me.

The only reason foreach over a dynamic array doesn't consume the slice is because foreach uses index based iteration on static and dynamic arrays and not the std.range.primitives functions.

It's not evident to me that the non-consuming behavior makes sense to map onto something completely different like containers that won't even necessarily have index based access.

It seems odd to me that slices would be the design basis when slices

1. Are intentionally limited in scope design wise to be a shrinking view of data
2. Only make sense behavior wise for array containers
3. Their ability to grow is a GC oddity which may end up copying a whole lot of data depending on the origin of the slice

There is no way to reclaim the original container. If you create a container, you get a range positioned to "see" the entire container. Once you popFront from that range, you lose all access to the first element in the container, unless you have a separate copy of the range. This is not new and not different from:

auto r = new int[10];
r.popFront;

What happens here is an array of 10 elements gets created. But you don't get a type "array of 10 integers". You get "a slice of integers, incidentally initialized to refer to the entire array of 10 integers that was just created". Next, you decide you don't care about the first element in the array. Once you call r.popFront, access to that element is lost forever.

First off, how are you going to do something like a map over a immutable container then, as map uses the range primitives and not foreach? There's no reason in principal that that should cause an issue. But with that design popFront is mutating the underlying data, and therefore should not be allowed. But this works with the std.container design because popFront is mutating the range view which has no reason to be immutable.

Secondly, if I want to say, perform a map over the elements of a container for some output, and I want to do things with the elements later in the code, then I have to create a copy of the container and pass the original to map? If that's the case then why not just go with the std.container behavior of getting a range from a primitive if you end up having to create range copies? I mean it's fundamentally the same, but that way you don't have to worry about the copies changing the data of the container out from under you.

What happens to the copy of a container when the original modifies the underlying data? For example, what should this code print?

    auto container = Array!int(MAllocator.instance);
    container.put(iota(10));

    auto container_copy = container;
    container.popBackN(5);
    container.insertBack(1);

    container_copy.each!(a => write(a, ", "));

In my estimation, a properly designed containers library should print

    0, 1, 2, 3, 4, 1, 5, 6, 7, 8, 9

But from what you're describing, that the containers act like slices, the code with print

    0, 1, 2, 3, 4, 1, 6, 7, 8, 9

Reply via email to