Is it a hash? Or is it a hash?
hash = {"Idiosyncratic" => "Ruby"}
hash["Idiosyncratic"] # => "Ruby"
hash.compare_by_identity
idiosyncratic_in_variable = "Idiosyncratic"
hash[idiosyncratic_in_variable] # => nil
hash["Idiosyncratic"] # => "Ruby"
Hash#compare_by_identity
changes the semantics of what is equal in a hash and what not. Only if exact the same object is passed in, the value will be retrieved.
Please note: The above example works for the key of"Idiosyncratic"
, because every string literal returns the same object. This behaviour has been introduced with Ruby 2.2, so running it in 2.1 or lower will return nil
for the last line.
Hash Surprise
Identity hashes can feel very unnatural as the following snippet illustrates:
hash = {}.compare_by_identity
a="Idiosyncratic"
b="Idiosyncratic"
c="Idiosyncratic"
hash[a] = "Ruby1"
hash[b] = "Ruby2"
hash[c] = "Ruby3"
hash #=> {"Idiosyncratic"=>"Ruby1",
# "Idiosyncratic"=>"Ruby2",
# "Idiosyncratic"=>"Ruby3"}
# Quiz: What is the result of: hash["Idiosyncratic"]
Explicit Identity Hashes
What can be done to improve readability and reduce surprise? A starting point can be using an extra class:
class IdentityHash < Hash
def initialize(*)
super.compare_by_identity
end
def inspect
"#<IdentityHash: #{super}>"
end
end
For this class to be really useful it would need some more Hash-like features, like ActiveSupport does with HashWithIndifferentAccess. Still, it is already less confusable than some hash that looks like a normal hash and that you haven't checked compare_by_identity?
for.
More Idiosyncratic Ruby
- Please Comment on GitHub
- Next Article: ERB Render Standard
- Previous Article: Utility Gems