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 receiverThe 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 greetThe 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 receiverImplicit receiver (self):
class MyClass
def method_a
method_b # Implicit receiver: self
end
def method_b
puts "called on #{self}"
end
endWhen 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 classThe 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 lookupThe 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!
endinstance_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)
endInside instance method:
class MyClass
def method
puts self # Instance of MyClass
end
endReceiver 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 receiverThis 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
endCall stack:
┌─────────────────────┐
│ method_b │
│ self: B instance │ ← Current receiver
├─────────────────────┤
│ method_a │
│ self: A instance │ ← Previous receiver
└─────────────────────┘