Not logged in (Log in or Sign up)

unwwwritten

On being CONSTANT

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