std.collection - changing the collection while iterating
Jonathan M Davis via Digitalmars-d
digitalmars-d at puremagic.com
Sun Jun 21 17:24:28 PDT 2015
On Sunday, 21 June 2015 at 23:02:38 UTC, Andrei Alexandrescu
wrote:
> While I work on making std.allocator better, here's some food
> for thought regarding std.collection.
>
> Consider a traditional container with reference semantics,
> Java-style. Regarding changing the collection (e.g.
> adding/removing elements) while iterating, we have the
> following possibilities:
>
> 1. Leave it undefined, like the STL does. Probably this is too
> extreme.
Personally, I have no problem with this whatsoever, but if that's
considered too extreme, then I'd go with
> 3. Allow the removal but throw from the ranges if there's any
> attempt to use a range following a remove.
but I'd make it throw an Error, not an Exception, since it'll
seriously harm @nogc code otherwise, and I don't see how it can
be considered anything but a logic error if you're continuing to
iterate over a container with a range or iterator after calling
erase/remove on the container, and it would invalidate the range
or iterator. I can see making it so that the range continues to
work if it wasn't invalidated by the removal (e.g. the removal
was at some point before or after the range in the container),
but if it was inside the range, then it should be considered a
logic error IMHO. To do otherwise would violate how ranges work,
because they currently guarantee that they only change in length
with calls to popFront or popBack. And in general, range-based
algorithms assume that even the values of elements don't change
while they're being iterated over, so having the range change
what elements it has without a call to popFront or popBack is
just begging for trouble.
The other thing to consider is reallocation (e.g. a vector type
which is appended to can end up being reallocated and invalidate
its iterators). And I think that that should be treated the same
way. Either we should just consider it undefined behavior or
throw an Error.
There are some rare cases where it makes sense to remove elements
from a container or add them while iterating over them, but in
general, I don't see how that could be considered anything but a
bad idea. Certainly, you have to understand exactly how the
container in question works and whether what you're doing works
with that type of container (e.g. adding or removing elements
from the beginning of a linked list while iterating over the end
of it would be no problem, but doing that with a vector would be
a disaster). The one place where it can get a bit annoying to not
be able to remove while iterating is when you're iterating over
an entire container and removing elements as you go along, but it
just doesn't make sense to expect a range to continue to work
when an element has been removed from it, since it fundamentally
violates how ranges work if how many elements they have changes
without a call to popFront or popBack.
However, in order to combat the case where you're iterating over
a container and removing elements from it as you go, we should
probably go the C++ route and have a remove function which
returns a range starting at the element after the last element in
the range that was passed to the remove function. Then, you can
have code like
for(auto range = container[]; !range.empty;)
{
//...
if(pred(range.front))
range = container.remove(takeOne(range));
else
range.popFront();
}
Similarly, we could have a remove function which takes a
predicate, which would be more idiomatic in many cases, but it
doesn't work in the general case, since that assumes that you're
not doing anything else while iterating and that the predicate
doesn't change while iterating.
Regardless, overall, I don't mind the C++ route with regards to
iterator invalidation, but if we're not going to go that route, I
think that we should go with throwing Errors when operating on
invalidated ranges. The resulting code is then essentially the
same, but it catches when you screw up your container logic.
- Jonathan M Davis
More information about the Digitalmars-d
mailing list