Bug #22021
closedArray#delete_if may delete wrong object if array has been altered already
Description
The simplest example I can come up with:
$ar = ar = [1, 2, 3, 4, 5]
def del(i)
$ar.delete(i)
end
ar.delete_if { |e| e == 2 ? (del(e) && true) : false }
p ar #=> [1, 4, 5], and it should be [1, 3, 4, 5]
Updated by Eregon (Benoit Daloze) about 1 month ago
- Description updated (diff)
Updated by Eregon (Benoit Daloze) about 1 month ago
ยท Edited
IMO it's not reasonable for Array#delete_if to do anything else than what it already does.
How can Array#delete_if know which element you deleted and whether it should delete or not?
The block returns true for the second 2nd block call, so it should delete the 2nd element after that block call (unless the Array#size < index when it's noop then).
Updated by Eregon (Benoit Daloze) about 1 month ago
- Status changed from Open to Rejected
I think this is safe to reject, I think it's not feasible to change this.
One should avoid mutating the Array while iterating, otherwise this kind of behavior is expected.
Updated by chucke (Tiago Cardoso) about 1 month ago
I get the point of avoiding mutating while iterating, but the point of delete_if is to mutate the array. there's nothing in its name hinting at it being based on the index, that's what #delete_at is for, so that's an implementation detail. I didn't check the implementation yet, so I may be oversimplifying it, but I don't see why the element ref can't be kept for post-comparison (although, if based on the #each impl, which is known to skip elements on mutation, I can understand your judgement).
Updated by Eregon (Benoit Daloze) about 1 month ago
The only way to iterate an Array is to use an index from 0 to size, so it's always going to be based on index.
chucke (Tiago Cardoso) wrote in #note-4:
I don't see why the element ref can't be kept for post-comparison
What if you have [1, 2, 2, 2, 2, 3, 4, 5, 2], how would delete_if find out if one of the 2 was removed concurrently?
There could also be another Thread writing to the Array, so checking if the element is the same before yielding to the block and after is inherently brittle and unreliable.