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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With