Declaring Constants
Problem
You want to prevent a variable from being assigned a different value after its initial definition.
Solution
Declare the variable as a constant. You can't absolutely prohibit the variable from being assigned a different value, but you can make Ruby generate a warning whenever that happens.
not_a_constant = 3 not_a_constant = 10 A_CONSTANT = 3 A_CONSTANT = 10 # warning: already initialized constant A_CONSTANT
Discussion
A constant variable is one whose name starts with a capital letter. By tradition, Ruby constant names consist entirely of capital letters, numbers, and underscores. Constants don't mesh well with Ruby's philosophy of unlimited changability: there's no way to absolutely prevent someone from changing your constant. However, they are a useful signal to the programmers who come after you, letting them know not to redefine a constant without a very good reason.
Constants can occur anywhere in code. If they appear within a class or module, you can access them from outside the class or module with the double-colon operator ( ::). The name of the class or module qualifies the name of the constant, preventing confusion with other constants that may have the same name but be defined in different scopes.
CONST = 4 module ConstModule CONST = 6 end class ConstHolder CONST = 8 def my_const return CONST end end CONST # => 4 ConstModule::CONST # => 6 ConstHolder::CONST # => 8 ConstHolder.new.my_const # => 8
The thing that's constant about a constant is its reference to an object. If you change the reference to point to a different object, you'll get a warning. Unfortunately, there's no way to tell Ruby to treat the redeclaration of a constant as an error.
E = 2.718281828 # => 2.718281828 E = 6 # warning: already initialized constant E E # => 6
However, you can use Module#remove_const as a sneaky way to "undeclare" a constant. You can then declare the constant again, without even triggering a warning. Clearly, this is potent and potentially dangerous stuff:
# This should make things a lot simpler. module Math remove_const(:PI) PI = 3 end Math::PI # => 3
If a constant points to a mutable object like an array or a string, the object itself can change without triggering the constant warning. You can prevent this by freezing the object to which the constant points:
RGB_COLORS = [:red, :green, :blue] # => [:red, :green, :blue] RGB_COLORS << :purple # => [:red, :green, :blue, :purple] RGB_COLORS = [:red, :green, :blue] # warning: already initialized constant RGB_GOLORS RGB_COLORS # => [:red, :green, :blue] RGB_COLORS.freeze RGB_COLORS << :purple # TypeError: can't modify frozen array
Freezing operates on the object, not the reference. It does nothing to prevent a constant reference from being assigned to another object.
HOURS_PER_DAY = 24 HOURS_PER_DAY.freeze # This does nothing since Fixnums are already immutable. HOURS_PER_DAY = 26 # warning: already initialized constant HOURS_PER_DAY HOURS_PER_DAY # => 26
See Also
- Recipe 8.15, "Freezing an Object to Prevent Changes"