Created
August 12, 2014 15:43
-
-
Save seanhandley/3bf9011ac0428bda4b49 to your computer and use it in GitHub Desktop.
Ruby Hash Constants are Anything But Constant
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
irb(main):001:0> I_AM_A_CONSTANT = "I never change" | |
=> "I never change" | |
irb(main):002:0> i_am_a_variable = I_AM_A_CONSTANT | |
=> "I never change" | |
irb(main):003:0> i_am_a_variable = "I can change" | |
=> "I can change" | |
irb(main):004:0> I_AM_A_CONSTANT | |
=> "I never change" | |
irb(main):005:0> I_AM_A_CONSTANT_HASH = {contents: "I never change"} | |
=> {:contents=>"I never change"} | |
irb(main):006:0> variable_hash = I_AM_A_CONSTANT_HASH | |
=> {:contents=>"I never change"} | |
irb(main):007:0> variable_hash[:contents] = "zomg!" | |
=> "zomg!" | |
irb(main):008:0> I_AM_A_CONSTANT_HASH | |
=> {:contents=>"zomg!"} | |
irb(main):009:0> |
Thanks @caius - I'm aware of the underpinnings and the way references work and my production code is already using #dup
and #freeze
.
What sucks is that these things are referred to as "constants" by all documentation and examples like this, contrived as they are, serve to show why people in functional languages care so much about immutability.
I love Ruby, but I imagine the thousands of bugs introduced to production codebases by this easily misinterpreted behaviour and my mind boggles. They shouldn't be called constants, they need a different name.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Technically all the constant does is make sure it doesn't get pointed at another object id. It doesn't make sure the object isn't mutated.
Your proof with a string is flawed, you're reassigning the
i_am_a_variable
rather than mutating the object. (Subtle diff!) A better example of that would be:Which then makes the hash mutation seem less surprising, you're mutating the object both the constant and variable have a reference to. If you want to stop accidental mutations of constants (or objects elsewhere) in this way, you need to freeze them:
And when you bump up against the frozen hash, you can just
#dup
it into the variable to make sure the copy of it is mutatable, and the original is left alone (which I think you discovered anyway.)