In a multithreaded context, even that isn't perfect; another thread can observe the array in an inconsistent state while you're mutating it. I wonder what Ruby does here?
If you're in a multithreaded context, then you need to handle locking of shared mutable memory; Ruby doesn't fix that for you (but it does make it pretty easy with stdlib Mutex and block syntax). MRI happens to make many basic operations thread-safe because of the GIL, but you shouldn't get into the habit of relying on that naively because it will stop working as you expect as soon as you start thinking of complex operations like reject! as 'just another atomic operation'.
Here's an example of how you'd manage the contention yourself:
my_array = [1, 2, 3]
my_array_lock = Mutex.new
Thread.new do
my_array_lock.synchronize do
my_array.reject! { |x| x % 2 == 0 }
end
end
Thread.new do
my_array_lock.synchronize do
my_array.push(4)
end
end
Thread.new do
my_array_lock.synchronize do
# Because we've serialised our operations on the array, there's no chance
# that we read the elements in an incorrect order
my_array.each do |x|
puts x
end
end
end