195

Here is some code:

class Person
  def initialize(age)
    @age = age
  end

  def age
    @age
  end

  def age_difference_with(other_person)
    (self.age - other_person.age).abs
  end

  protected :age
end

What I want to know is the difference between using @age and self.age in age_difference_with method.

evuez
  • 3,257
  • 4
  • 29
  • 44
sarunw
  • 8,036
  • 11
  • 48
  • 84

7 Answers7

288

Writing @age directly accesses the instance variable @age. Writing self.age tells the object to send itself the message age, which will usually return the instance variable @age — but could do any number of other things depending on how the age method is implemented in a given subclass. For example, you might have a MiddleAgedSocialite class that always reports its age 10 years younger than it actually is. Or more practically, a PersistentPerson class might lazily read that data from a persistent store, cache all its persistent data in a hash.

Chuck
  • 234,037
  • 30
  • 302
  • 389
  • 2
    I have once read a book in rails and don't understand the difference between this self and @, so I should always use self.var_name in my methods (that doesn't setter and getter) to make my data using public interface, I spent time defining it in getter and setter, right ? – sarunw Nov 07 '09 at 15:35
  • 2
    ...english... what do you mean by any number of things. i didnot get that last two examples given. – user2167582 Oct 06 '14 at 02:43
  • I think it's more clear if you refer to `age` without `@age`. `age` *could* come in two flavors. `(attr) age` and `(method) age`. calling self.age will call `(method) age` which normally is just `(attr) age` but `@age` calls `(attr) age` even if `(method) age` exists. – financial_physician Jun 30 '22 at 19:42
24

The difference is that it is isolating the use of the method from the implementation of it. If the implementation of the property were to change -- say to keep the birthdate and then calculate age based on the difference in time between now and the birthdate -- then the code depending on the method doesn't need to change. If it used the property directly, then the change would need to propagate to other areas of the code. In this sense, using the property directly is more fragile than using the class-provided interface to it.

tvanfosson
  • 524,688
  • 99
  • 697
  • 795
12

The first answer is entirely correct, but as a relative newbie it wasn't immediately clear to me what it implied (sending messages to self? uh huh...). I think that a short example will help:

class CrazyAccessors
  def bar=(val)
    @bar = val - 20 # sets @bar to (input - 20)
  end
  def bar
    @bar
  end

  def baz=(value)
    self.bar = value # goes through `bar=` method, so @bar = (50 - 20)
  end

  def quux=(value)
    @bar = value     # sets @bar directly to 50
  end
end

obj  = CrazyAccessors.new
obj.baz = 50
obj.bar  # => 30
obj.quux = 50
obj.bar  # => 50
blob
  • 425
  • 1
  • 4
  • 9
  • 12
    This example made things more confusing. – Oskar Holmkratz Jul 30 '16 at 22:25
  • 2
    I am sorry but the example is not commented enough for me. I cannot follow your reasoning. – kouty Dec 11 '16 at 19:04
  • Someone who came from Smalltalk will say that an object "sends a message to itself." Someone who came from Python will say that an object "calls a method on itself." Don't be confused; they are exactly the same thing. (A semantics purist may object that they are only the same for languages with dynamic typing and that a C++ virtual method call isn't exactly the same as sending a message. The purist is correct, but that's probably beyond the scope of this question/answer.) – GrandOpener Mar 22 '19 at 19:19
  • I like the example but please provide some more comments as to what's actually happening. Hard to follow with no explanation – CalamityAdam Jun 19 '19 at 15:19
  • Looks good to me in 2022 – financial_physician Jun 30 '22 at 19:37
11

Be warned when you inherit a class from Struct.new which is a neat way to generate an intializer (How to generate initializer in Ruby?)

class Node < Struct.new(:value)
    def initialize(value)
        @value = value
    end
    def show()
        p @value
        p self.value # or `p value`
    end
end 

n = Node.new(30)
n.show()

will return

30
nil

However, when you remove the initializer, it will return

nil
30

With the class definition

class Node2
    attr_accessor :value
    def initialize(value)
        @value = value
    end
    def show()
        p @value
        p self.value
    end
end

You should provide the constructor.

n2 = Node2.new(30)
n2.show()

will return

30
30
Community
  • 1
  • 1
prosseek
  • 182,215
  • 215
  • 566
  • 871
  • 2
    Thanks for the example @Prosseek, I am currently learning Ruby on Rails and this is exactly the kind of behavior that makes me feel Ruby is unnecessarily complicated >.<. – cyc115 Jun 10 '18 at 18:16
2

There isn't any difference. I suspect that it was done just for the documentary value of seeing self.age and other_person.age near each other.

I suppose that use does allow for an actual getter to be written in the future, which might do something more complex than just return an instance variable, and in that case the method would not need to change.

But that's an unlikely abstraction to worry about, after all, if the implementation of the object changed it's reasonable to change other methods, at some point a simple reference within the object itself is perfectly reasonable.

In any case, abstraction of the age property still doesn't explain the explicit use of self, as just plain age would also have invoked the accessor.

DigitalRoss
  • 143,651
  • 25
  • 248
  • 329
1

self.age refers to a getter method of @age. Consider the following example:

class Person
  def initialize(age)
    @age = age
  end

  # def age
  #   @age
  # end

  def age_difference_with_1(other_person_age)
    (@age - other_person_age).abs
  end

  def age_difference_with_2(other_person_age)
    (self.age - other_person_age).abs
  end
end

person = Person.new(25)
person.age_difference_with_1(40)
# => 15
person.age_difference_with_2(40)
# NoMethodError (undefined method `age' for #<Person:0x0000000108034298 @age=25>)

Notice person.age_difference_with_2(40) raises NoMethodError because the corresponding getter method age doesn't exist. This exception disappears after uncommenting:

  def age
    @age
  end

Alternatively, you can also define a getter method with this line instead:

  attr_reader :age
molexi
  • 530
  • 5
  • 13
-3

@age - is definitely the instance variable age

self.age - refers to the instance property age.

LEMUEL ADANE
  • 8,336
  • 16
  • 58
  • 72