To answer your question whether it is safe to do this, you first need to determine what you mean by "safe." You mean
- Is this a runtime failure?
- it does not
raise a Exception ? - does he
raise a Exception ? - behaves deterministically?
- Does he do what you expect from this? (What do you expect from this?)
Unfortunately, the Ruby language specification is not entirely useful:
15.2.12.5.10 Array # each
each (& block)
Visibility : public
Behavior
- If a block is specified:
- For each recipient element in index order, a call block with the element as its only argument.
- Return the receiver.
This, apparently, means that it is really completely safe in the sense of above 1., 2., 4. and 5.
The documentation says:
each { |item| block } each { |item| block } β ary
Calls this block once for each element in self , passing this element as a parameter.
Again, this seems to imply the same thing as the spec.
Unfortunately, none of the existing Ruby implementations interpret the specification in this way.
What actually happens in MRI and YARV is the following: an array mutation, including any offset of elements and / or indices, becomes immediately visible, including in the internal implementation of iterator code, which is based on array indices. Thus, if you delete an element at or before the position in which you are currently iterating, you will skip the next element, whereas if you delete an element after the position at which you are currently iterating, you will skip that element. For each_with_index you will also notice that all elements after the deleted element have their indices shifted (or, conversely, vice versa: the indices remain, but the elements are shifted).
Thus, this behavior is βsafeβ in the sense of 1., 2., and 4.
Other Ruby implementations basically copy this (undocumented) behavior, but being undocumented you cannot rely on it, and in fact I believe that at least one of them experimented briefly with raising a kind of ConcurrentModificationException .
JΓΆrg W Mittag
source share