Reputation: 1520
Under certain conditions Hash#keys
does not work correctly in Ruby before version 2.4
Demo code:
h = { a: 1, b: 2, c: 3 }
h.each do |k, v|
h.delete(:a)
p h
p h.keys
break
end
Ruby 2.3.8 output:
{:b=>2, :c=>3}
[:b]
Ruby 2.5.1 output:
{:b=>2, :c=>3}
[:b, :c]
I agree it is not good to modify hash when iterating. But I did not see the relation between the modification the hash and the work keys method.
Why is this happening?
Upvotes: 10
Views: 147
Reputation: 54233
Interesting question. This isn't an answer yet, but it's too long for a comment and it could help others answer the question.
I created a GitHub repository with a very simple spec:
describe Hash do
it "should always know which keys are left" do
h = { a: 1, b: 2, c: 3 }
h.each do |k, v|
h.delete :a
expect(h.keys).to eq [:b, :c]
end
end
end
Thanks to Travis, it's easy to see which Ruby versions have this bug:
I just spent an hour using git bisect
and make install
in order to find that the bug has been fixed in this commit (75775157).
Introduce table improvement by Vladimir Makarov .
[Feature #12142] See header of st.c for improvment details.
You can see all of code history here: https://github.com/vnmakarov/ruby/tree/hash_tables_with_open_addressing
This improvement is discussed at https://bugs.ruby-lang.org/issues/12142 with many people, especially with Yura Sokolov.
st.c: improve st_table.
include/ruby/st.h: ditto.
internal.h, numeric.c, hash.c (rb_dbl_long_hash): extract a function.
ext/-test-/st/foreach/foreach.c: catch up this change.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@56650 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
It has been confirmed by @Vovan, who found this commit 1 minute before I did.
Upvotes: 7