The receiver is the object on which a method is being called. In object-oriented programming, methods are invoked on objects, and that object is the receiver.

What is a Receiver?

object.method_name    # 'object' is the receiver
 
"hello".upcase        # "hello" is the receiver
[1,2,3].length        # [1,2,3] is the receiver
user.save             # user is the receiver

The receiver is the object that receives the message (method call).

Receiver as ‘self’

Inside a method, the receiver becomes self:

class User
  def greet
    puts "I am #{self.class}"    # 'self' is the receiver
    puts self.object_id
  end
end
 
user = User.new
user.greet    # user is the receiver, becomes 'self' inside greet

The receiver is the context in which the method executes.

Receiver in YARV

In YARV, the receiver is tracked in the frame:

# Ruby code:
object.method_name
 
# YARV bytecode (simplified):
getlocal object           # Push receiver onto stack
opt_send_without_block <calldata for method_name>

The stack holds the receiver before method dispatch occurs. The receiver is then stored in the callee’s frame as self.

Explicit vs Implicit Receivers

Explicit receiver:

user.name    # 'user' is explicit receiver
object.save  # 'object' is explicit receiver

Implicit receiver (self):

class MyClass
  def method_a
    method_b    # Implicit receiver: self
  end
 
  def method_b
    puts "called on #{self}"
  end
end

When no receiver is specified, Ruby uses self as the receiver.

Receiver in Method Dispatch

Finding the right method involves looking at the receiver’s class:

class Dog
  def speak
    "woof"
  end
end
 
class Cat
  def speak
    "meow"
  end
end
 
dog = Dog.new
dog.speak    # Receiver: dog (a Dog) → look up 'speak' in Dog class
 
cat = Cat.new
cat.speak    # Receiver: cat (a Cat) → look up 'speak' in Cat class

The receiver’s class determines which method is called. See virtual machine architecture for method lookup.

Receiver and Inline Caching

YARV optimizes method calls by caching based on receiver type:

# First call at this call site:
user.name    # Lookup 'name' on User class, cache result
 
# Second call at same call site:
user.name    # If user is still a User, use cached lookup

The call site caches method lookups for specific receiver classes. If the receiver’s class changes, the cache must be invalidated.

Changing the Receiver

Some methods change the receiver:

class Context
  def execute(&block)
    instance_eval(&block)    # Changes receiver to self
  end
end
 
context = Context.new
context.execute do
  puts self.class    # 'self' (receiver) is Context, not main!
end

instance_eval changes the receiver for the block’s execution.

Receiver in Different Contexts

Top-level:

puts self    # main (the top-level object)

Inside class definition:

class MyClass
  puts self    # MyClass (the class itself is the receiver)
end

Inside instance method:

class MyClass
  def method
    puts self    # Instance of MyClass
  end
end

Receiver and Private Methods

Private methods can only be called with implicit receiver:

class MyClass
  def public_method
    private_method    # OK: implicit receiver (self)
  end
 
  private
 
  def private_method
    "private"
  end
end
 
obj = MyClass.new
obj.private_method    # Error: private method called with explicit receiver

This restriction is based on the receiver being explicit vs implicit.

Receiver in Call Stack

Each frame on the call stack has its own receiver:

class A
  def method_a
    b = B.new
    b.method_b    # Create new frame with b as receiver
  end
end
 
class B
  def method_b
    # self (receiver) is the B instance
  end
end

Call stack:

┌─────────────────────┐
│  method_b           │
│  self: B instance   │ ← Current receiver
├─────────────────────┤
│  method_a           │
│  self: A instance   │ ← Previous receiver
└─────────────────────┘