Using self in Ruby comes naturally if you’ve been writing Ruby for any length of time. It’s easy to gloss over its meaning and significance, however.

Take, for instance, the following class.

class Car
  attr_accessor :make

  def initialize(make)
    @make = make
  end

  def blend_in(new_make)
    make = new_make
  end
end
car = Car.new('Chevy')
car.blend_in('Porsche')
#=> "Porsche"
car.make
#=> "Chevy"

Why didn’t #blend_in work as expected? We missed a self. We should have written self.make = new_make.

It’s easy and sometimes tempting to memorize heuristics like “always use self. when calling a setter.” While rote memorization can help keep beginners from getting mired in the details, at some point it’s important to dig into the why.

Obviously if we leave off self., we’re assigning a local variable. But why does Ruby do this?

Ruby specifically tries to make setters look like regular variable assignments. In most cases, this makes Ruby very simple and elegant. It can also introduce ambiguity. Does make = new_make mean #make=(new_make) or does it mean ‘assign the value of new_make to a new local instance variable called make?’

There’s no way for Ruby to know. Did you notice I clarified the second case by writing it out in English? That’s because there’s no clearer, more unambiguous way to write it in Ruby. Because there’s no other way to write it, if Ruby defaulted to using a setter when one existed, there would be no way to define a local variable with the same name as a setter.

While it’s generally not good practice to do so, there are cases where it might be useful.

Using self in Ruby getters

So why don’t you need to use self. when calling a getter? In some cases, you do!

class Car
  attr_accessor :make

  def blend_in(new_make)
    make = new_make
    puts make
    puts self.make
  end
end
car = Car.new
car.make = "Chevy"
car.blend_in("Jaguar")
# prints "Jaguar" for make and "Chevy" for self.make