unwwwritten
On being CONSTANT
Posted November 7th, 2007 at 8:24 pm EST by S. Brent Faulkner — View Comments
So, I'm working on the syncer and stumbled upon something that surprised me (probably due to my C and C++ background). Constants are not constant in ruby. What I mean is that there is little to no protection against modifying them. Now, this is apparently a known fact in php too, but I was not aware of it in ruby. Truth be told, I guess it's not completely true in C or C++ either. A simple cast can make almost anything volatile, unless of course the compiler and/or platform uses memory protection to really make things const.
However, on this occasion, I got a little surprise. Given the following code:
class Base DEFAULT = [ "DEFAULT VALUE" ] end class A < Base def initialize @array = DEFAULT @array << "A" puts @array.inspect end end class B < Base def initialize @array = DEFAULT @array << "B" puts @array.inspect end end a = A.new b = B.new
I did not expect this:
["DEFAULT VALUE", "A"] ["DEFAULT VALUE", "A", "B"]
Thanks to the fact that ruby variables are references, the two instance variables for the A and B classes both point to the same object -- the constant. Had it not been a constant that would have made sense to me. However, as a constant I sort of expected a copy to be automatically made for me. The modifications made to the instance variables are actually modifying the constant.
I played around a bit, and I think a safer way to use constants is to use freeze on them. Then when assigning them to something, use dup to get a "thawed" copy. If you forget to dup the constant, you'll get a nice exception when you accidentally modify the constant.
For example, by doing the following:
class Base DEFAULT = [ "DEFAULT VALUE" ].freeze end class A < Base def initialize @array = DEFAULT.dup @array << "A" puts @array.inspect end end class B < Base def initialize @array = DEFAULT.dup @array << "B" puts @array.inspect end end a = A.new b = B.new
You can now get my expected output:
["DEFAULT VALUE", "A"] ["DEFAULT VALUE", "B"]
Thanks for listening.
blog comments powered by Disqus