Differencebetween a Constant and a Variable in Ruby

Difference between constant variables and global variables

Global variables are global, meaning that even if you put them in a class which is very specifically scoped, they're still available everywhere. They are also explicitly variables (meaning one should not be surprised if their value changes).

For example:

module TopLevel
module MiddleLevel
module LowLevel
class SpecificSomething

$my_global = "duff man says a lot of things"

end
end
end
end

module TopLevel
def self.global
p $my_global
end
end

TopLevel.global
#=> "duff man says a lot of things"

Constants are accessible where they are defined - that is, they are NOT global. They are also constants (as the link you provided points out), so one would NOT expect them to change (although ruby does allow them to be changed).

Constants vs instance variable

Consider this class:

class Dog
NUMBER_OF_LEGS = 4

@dog_counter = 0

attr_reader :name

def initialize(name)
@name = name
end

def legs
NUMBER_OF_LEGS
end
end

Here, NUMBER_OF_LEGS is a constant, @name is an instance variable (with a getter method), and @dog_counter is what's called a class instance variable.

In Ruby everything is an object, even classes, and as such they can have their own instance variables.

Look at how we could use this class:

dog = Dog.new('Chewbacca')
dog.name
# => 'Chewbacca'

dog.legs
# => 4
Dog::NUMBER_OF_LEGS
# => 4

That's fine, but we do not have a direct interface to access @dog_counter. The only way to do something with it is to use introspection methods:

dog.class.instance_variable_get(:@dog_counter)
# => 0
dog.class.instance_variable_set(:@dog_counter, 1)

dog.class.instance_variable_get(:@dog_counter)
# => 1

dog.class.instance_eval { @dog_counter = 10 }
dog.class.instance_variable_get(:@dog_counter)
# => 10

We can do better than that. Look at this other implementation:

class Dog
@dog_counter = 0

attr_reader :name

class << self
attr_accessor :dog_counter
end

def initialize(name)
@name = name
self.class.dog_counter += 1
end

end

Now we have defined a class accessor (setter and getter), and we're also incrementing it with each new instance. The interface is simple:

Dog.dog_counter
# => 0

dog_1 = Dog.new('Han')
dog_2 = Dog.new('Luke')

Dog.dog_counter
# => 2
dog_2.class.dog_counter
# => 2

As to proper class variables, they are scoped on the class and can be accessed by instances.

The big problem, however, is that they are shared between all classes in the same hierarchy. Each class that sets a new value will update it for all its ancestors and descendants.

For this reason they are generally avoided, and class instance variables are preferred (they are class specific).

class Scientist
@@greet = "Hello, I'm a Scientist!"

def greet
@@greet
end
end

class Biologist < Scientist
@@greet = "Hello, I'm a Biologist!"
end

class Physicist < Scientist
@@greet = "Hello, I'm a Physicist!"
end

class ParticlePhysicist < Physicist
@@greet = "Hello, I'm a ParticlePhysicist!"
end

biologist = Biologist.new
biologist.greet
# => "Hello, I'm a ParticlePhysicist!"

Constants or class variables in ruby?

The main thing is that by using the CONSTANT notation, you're making it clear to the reader. the lower case, frozen string gives the impression is might be settable, forcing someone to go back and read the RDoc.

What is the difference in scoping between a global variable and a constant in Ruby?

Global variables are the ones that can be accessed from anywhere. Their scope turns to be the whole of the main object. This means they can be used anywhere in this scope i.e anywhere in the code itself. For instance

module A
module B
class C
$glo = 'this is glo-bal variable'
end
end
end

module D
class E
CON = 'this is con-stant'
def call_glo
puts $glo
end

def call_con
puts CON
end
end

def self.call_con
puts CON
end

E.new.call_glo #=> "this is glo-bal variable"
end

D::E.new.call_glo #=> "this is glo-bal variable"
D::E.new.call_con #=> "this is con-stant"
D.call_con #=> Throws Error Unitialized Constant

While the constants are restricted to the scope they are defined in. They can only be used in the scope they are defined.

Now, as you said Constants starts with capitals, hence all the class names and module names are themselves nothing but Constants.

Now in the above example, you see the call_glo method is called twice. Once from the scope of module D while one from the main object scope, do you see the difference between the instantiation of class E?

In module D it is called without any scope operator :: while outside of module we had to use the scope operator, that is the restriction of scope. Constants are bound to.

When to use constants instead of instance variables in Ruby?

There's a few things to consider here:

  • Will the value change within the life-cycle of an object?
  • Will you need to override the value in sub-classes?
  • Do you need to configure the value at run-time?

The best kind of constants are those that don't really change short of updating the software:

class ExampleClass
STATES = %i[
off
on
broken
].freeze
end

Generally you use these constants internally in the class and avoid sharing them. When you share them you're limited in how they're used. For example, if another class referenced ExampleClass::STATES then you can't change that structure without changing other code.

You can make this more abstract by providing an interface:

class ExampleClass
def self.states
STATES
end
end

If you change the structure of that constant in the future you can always preserve the old behaviour:

class ExampleClass
STATES = {
on: 'On',
off: 'Off',
broken: 'Broken'
}.freeze

def self.states
STATES.keys
end
end

When you're talking about instance variables you mean things you can configure:

class ConfigurableClass
INITIAL_STATE_DEFAULT = :off

def self.initial_state
@initial_state || INITIAL_STATE_DEFAULT
end

def self.initial_state=(value)
@initial_state = value ? value.to_sym
end
end

Constants are great in that they're defined once and used for the duration of the process, so technically they're faster. Instance variables are still pretty quick, and are often a necessity as illustrated above.

Constants in Ruby and Java's final keyword

Like a lot of things in Ruby there is no "final", things are inherently dynamic and absolutely preventing people from doing things is never really going to happen. You can just make it difficult.

The one thing to note is in Ruby there's a difference between immutable and constant. A constant is a variable which will generate a warning when reassigned, that's all, and there's nothing to prevent you from modifying it. To prevent modifications you must "freeze" the object in question, though as with all things in Ruby, this is just a request that can be ignored by the object.

Typically you'll see code like this:

ADMIN_USER_TYPE = 'Admin'.freeze

Or this:

USER_TYPES = %w[
Admin
].freeze

The freeze call is to catch cases where the list might be mangled by some method by accident. It does not absolutely prevent this, it's more of a safety measure. Consider this code:

def user_labels(types)
types.map! { |t| [ t, t.downcase.to_sym ] }
end

Here a mistaken map! call would have the effect of rewriting the original array. In cases where you're calling it with a throw-away argument this is fine:

user_labels(%w[ Admin Test ])

When you're using the constant you'll permanently modify it, and that will cause it to get modified over and over each time it's called, creating a mess. The freeze flag trips a warning here and prevents that.

So the short answer is: No. The long answer is you have to be disciplined, the language will not prevent you from doing this if you're sufficiently determined. Pay attention to warnings and treat them seriously.

What does it mean when a variable or method or constants has preceded with an underscore in Ruby?

There is no difference in bahavior. It is just convertion.

But let's have a closer look:

_('A string') is actually a method not a variable. The underscore method is defined by Gettext and translates a string.

@_foo is offen used to show other developers that there is something special about the variable, and therefore it should not be used. I saw that pattern a lot for variables that are used to cache values, like:

def expensive_operation
@_expensive_operation ||= begin
# long running code...
end
end

And the underscore is sometimes used to indicate that a variable is not used in a block. Like this:

a_hash.each do |_, value|
# do something with the value, not with the key
end

How do you use global variables or constant values in Ruby?

One thing you need to realize is in Ruby everything is an object. Given that, if you don't define your methods within Module or Class, Ruby will put it within the Object class. So, your code will be local to the Object scope.

A typical approach on Object Oriented Programming is encapsulate all logic within a class:

class Point
attr_accessor :x, :y

# If we don't specify coordinates, we start at 0.
def initialize(x = 0, y = 0)
# Notice that `@` indicates instance variables.
@x = x
@y = y
end

# Here we override the `+' operator.
def +(point)
Point.new(self.x + point.x, self.y + point.y)
end

# Here we draw the point.
def draw(offset = nil)
if offset.nil?
new_point = self
else
new_point = self + offset
end
new_point.draw_absolute
end

def draw_absolute
puts "x: #{self.x}, y: #{self.y}"
end
end

first_point = Point.new(100, 200)
second_point = Point.new(3, 4)

second_point.draw(first_point)

Hope this clarifies a bit.



Related Topics



Leave a reply



Submit