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