When do I need to use self.instance_method vs instance_method alone?

157 Views Asked by At

I'm hoping someone can explain why I get the same behavior from both of these methods and what that means for when to or not to use self.

def my_instance_method(arg)
  'execute some code' if another_instance_method_of_same_class?(arg)
end

seems to behave exactly the same as

def my_instance_method(arg)
  'execute some code' if self.another_instance_method_of_same_class?(arg)
end

My apologies if my search skills aren't up to par but I couldn't find the exact answer to this question, just explanations of what self does (which made me think I should need it). I'm using the latest version of Ruby. Thank you.

2

There are 2 best solutions below

3
On BEST ANSWER

There are a few differences between self.foo(...) and foo(...), but they're mostly equivalent.

Privacy

private methods can only be called via foo directly, and never with an explicit receiver. So if foo is marked private, then you have to call it without self.

class Example

  private def foo(x)
    x + 1
  end

  def bar
    foo # Works fine
    self.foo # Error: foo is private
  end

end

Shadowing

If you have a local variable called foo in the current function, then writing foo without arguments will reference the local variable instead

class Example

  def foo(*args)
    puts "Hello :)"
  end

  def bar
    foo = 100 # Just an ordinary variable; no relation to the above method
    foo # This refers to the local variable called "foo"
    self.foo # This calls the method foo() with no arguments
    foo(1) # This calls the method foo() with one argument
    self.foo(1) # Equivalent to the above; calls with one argument
  end

  def baz
    foo # There's no local variable called "foo", so this calls the method
  end

end

Assignment

If the method you're talking about is an assignment method (i.e. ends in =), then it must always be called with an explicit receiver

class Example

  def foo=(value)
    puts "Assigning foo = #{value}"
  end

  def bar
    foo = 0 # This creates a new local variable called foo and assigns to it
    self.foo = 0 # Calls foo= on self
  end

end
1
On

I will answer your question, but that will not help you understand when you are to use the prefix self. when defining methods. I therefore need to first present some background information on instance methods.

Suppose we have the following class definition.

class C
  puts "self = #{self}

  def i
    puts "self in i = #{self}"
    "i"
  end

  def self.c
    puts "self in c = #{self}"
    "c"
  end

  singleton_class.define_method(:s) do
    puts "self in s = #{self}"
    "s"
  end
end
  #=> :s
  self = C

I have defined an instance method C#i, a class method C::c and an instance method on C singleton class, s.

Note that, since self #=> C within the class, writing def self.c ... is precisely the same as def C.c .... The only reasons for writing self is that if we rename the class we don't have to change def self.c ... and "self" is harder to misspell than, say, "PhilharmonicOrchestras".

Unsurprisingly,

C.instance_methods(false)
  #=> [:i]
instance = C.new
  #=> #<C:0x00007ff0011074f0>
instance.i
  #=> "i"
  self in i = #<C:0x00007ff0011074f0>

Now consider the last two methods:

C.methods(false)
  #=> [:s, :c]
C.c
  #=> "c"
  self in c = C
C.singleton_class.instance_methods(false)
  #=> [:s, :c]
C.s
  #=> "s"
  self in s = C

This shows that defining a method def self.class.c is merely a shorthand way of telling Ruby that you wish to create an instance method s on C's singleton class. Similarly, references to "class methods" are shorthand for those same instance methods. In fact, there is no need to define methods with the prefix self.. You could instead use singleton_class and define_method, as I have done, or elect one of several other ways, the most common being

class C
  class << self
    def s
      puts "self in s = #{self}"
      "s"
    end
  end
end

where class << self .. end causes self to change from C to C.singleton_class.


You defined a method which I will modify a bit:

self
  #=> main

def my_instance_method
  puts "self = #{self}, self.class = #{self.class}"
  'execute some code'
end
my_instance_method
  #=> "execute some code"
  self = main, self.class = Object

As all methods are instance methods this must be one as well, namely a private instance method of Object:

Object.private_instance_methods.include?(:my_instance_method)
  #=> true

Being a private instance method we cannot invoke it on an explicit receiver that is an instance of Object (main), but we can write:

Object.new.send(:my_instance_method)
  #=> "execute some code"
  self = #<Object:0x00007f988514e180>, self.class = Object

Note that if it were made a public method it would be available for use by all objects, which obviously would lead to disaster.

Next consider:

def self.your_instance_method
  puts "self = #{self}, self.class = #{self.class}"
  'execute some code'
end
your_instance_method
  #=> "execute some code"
  self = main, self.class = Object

This method must be defined on self's (main's) singleton class, which we may confirm:

self.singleton_class.instance_methods.include?(:your_instance_method)
  #=> true

Therefore, the implicit receiver of

your_instance_method

is self, which equals main, giving the same result as

self.send(:your_instance_method) 

This is a complicated subject (concerning Ruby's Object Model), particularly for someone fairly new to Ruby, so don't be concerned if you are not able to follow all of what I have said.