BLOG / 2012-06-13

Instance Variables as Syntax Sugar

Update, November 2026: Ruby never made @ a hash, but Ruby 2.6’s to_h with a block does shorten the serialization loop below: instance_variables.to_h { |v| [v, instance_variable_get(v)] }. Facets had something close as Kernel#instance; it’s now the separate instance gem.

What are instance variables? If we think of them as a single unit we note that they represent an object state, which as a data structure is really nothing more than a hash. Indeed, it is quite feasible to forgo the use of instance variables altogether save one, to serve as the object’s state.

class Example
  def self.attr_reader(name)
    define_method(name){ @state[name.to_sym] }
  end

  def self.attr_writer(name)
    define_method("#{name}="){ |v| @state[name.to_sym] = v }
  end

  def self.attr_accessor(name)
    attr_reader(name)
    attr_writer(name)
  end

  def initialize
    @state = {}
  end
end

So if instance variables are essentially a hash, why is it that we cannot work with them as such? In other words, why not have @ just represent a syntax sugar for a special hash (even if it’s not a standard Ruby Hash instance under the hood, it can behave as if it were). With such a change, handling instance variables immediately becomes much easier and intuitive since we can apply all our knowledge of hashes.

Some neat capabilities immediately become apparent. Looking up an instance variable given a variable name can be done with:

@[name]

Assigning an instance variable likewise can be done with:

@[name] = value

No longer would the long-winded instance_variable_get and instance_variable_set be needed.

Let’s take a more complex example. Have you ever wanted to reproduce the name and values of an object instance variable? It’s not uncommon really. Any type of object serialization is going to need such a routine – YAML, Marshal, etc. To do this presently requires some verbose code:

hash = {}
instance_variables.each do |var|
  hash[var] = instance_variable_get(var)
end
hash

But with @ sugar we can simply use:

hash = @.to_h

Or if we just want to iterate over them:

@.each do |k,v|
  ...
end

Since we have #each, then all Enumerable methods would be at our disposal.

For a public interface instance_variables could reference the internal @ instead of just the list of instance variable names. We could deprecate #instance_variable_get and #instance_variable_set altogether.

By recognizing the nature of instance variables and applying the preexisting functionality of Hash, we gain much greater capability while using fewer brain cells to do it. And that’s always a win.

Thomas Sawyer · Published
← All posts