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
└─────────────────────┘