Self vs @: its a decision, not a rule.

As a new programming student, I grasp for black and white rules to follow when learning a new concept, and the case of self vs. @ was no different. It was not clear when I should use self and when I should use @ to access instance variables. I researched in books and online, looking for a rule that would eliminate my confusion. Instead of finding one answer, I found a whole variety. Unfortunately, for the beginner in me at this stage, it seems there is no straight answer to this question.

What I realized, however, is self vs. @ is a question and a decision I will face every time I write a custom class and/or instance method in object oriented programming. This fact points to a subtler side of problem solving that web development requires and illuminates a next level of sorts in my learning: designing code. If level one is you can find a solution to the problem, this next level is you can find multiple solutions from which you’ll pick the best option. Designing code is understanding the decisions you face as you program and knowing the advantages and disadvantages of your options. This is what is required when faced with the situation of self vs. @.

So, let’s take a closer look…

The decision: use self.var or @var

First, understand the fundamental difference: the former calls a method that returns the instance variable, and the latter does not — it is the instance variable.

What can be confusing is that self.var appears to be calling self.instance_var because it happens to share the same name as the instance_var. In actuality, however, self.var calls self.a_method. In the scenario of accessing an instance variable, a_method is the getter or setter method. In order to use self, these getter and/or setter methods need to exist either explicitly with a method you write or implicitly with attr_reader, attr_writer, or attr_accessor.

Its easy to skip over the word ‘self’ altogether and just know that it is necessary in the same way ‘@’ is necessary to indicate an instance variable. self, however, is actually doing a little more. self references the object.

For example…

class Dog
attr_reader :breed
  def initialize(breed)
@breed = breed
end
  def display_breed
puts self.breed
end
end
fido = Dog.new('beagle')
fido.display_breed

In the code above, we have a class of Dog, and we are choosing to do two things: 1.) make a new instance of Dog, fido = Dog.new('beagle') and 2.) call the display_breed method on fido. When we do #1, fido (as well as any instance of Dog) is initialized with an instance variable@breed whose value is passed in as an argument. When we do #2, the display_breed method prints self.breed. What is self.breed though? Here self refers to the object fido, which calls the implicit breed getter method. This getter method holds the value of @breed: ‘beagle’. So display_breed prints beagle.

Secondly, consider the trade-offs of each option:
A. Private vs. Public

self.var is public, and therefore can be accessed outside the class. Depending on the circumstance, this public state could cause problems because the data is no longer protected. Note: one exception is if the getter and setter methods are explicitly made private with the ‘private’ keyword. In this case, these methods cannot be accessed outside the class.

@var is private, and therefore cannot be accessed outside the class. Obviously, this private state puts limitations on the code, which is a key concept in object oriented programming.

B. Maintenance/Encapsulation/Custom logic

self can actually be used on any instance method not just the accessor methods. One advantage to using self is any custom logic pertaining to an instance variable is easier to maintain as the code is written in one place.

Writing this custom logic with @var would require you to make changes in many places in your code when a revision is required at a later time.

C. Cleanliness/Readability

Code littered with self can be harder to read due to the added text.

Note 1: self is not required for getter methods. In the example of fido, the display_breed method could have been written as:

def display_breed
puts breed
end

Note 2: self is required for calling setter methods. Without self, the syntax of assigning a value to the instance variable via the setter method is the same as the syntax of assigning a value to a local variable method_or_var = value, and Ruby would interpret the value as a local variable instead.

D. Performance
 
 @var is faster.

Lastly, decide what is the most important factor to accommodate in your code. Over time, a developer might consistently choose one factor as more important than others, and this becomes his/her preference.

Which factors do you deem as most important to consider when deciding to use self or @? What is your preference?