Yield Keyword in Ruby
As the main technique by which a method calls a code block related to its invocation, the yield
keyword is an essential component of Ruby. Through this technique, methods can develop unique callback systems, resource management, and looping constructs collectively referred to as iterators.
By temporarily returning control from the executing method to the associated code block, yield
essentially functions as a special method call.
Function and Control Flow of yield
When yield
is executed by an iterator method, control instantly moves to the code block linked to that method call. Control returns to the method at the statement that comes just after the yield
statement, where the block’s execution ends.
Iteration or looping behaviour can be implemented by methods by calling yield
more than once.
Ruby’s strong iterator methods are based on the ability to relate any method call to arbitrary code (the block) via yield
. Techniques that employ yield
are frequently referred to as generators or iterators.
Code Example: Simple Transfer of Control
To accept a block, a yield
method does not require extra syntax; it only has to be defined normally.
def simple_call
puts "Start of method"
yield # Passes control to the block
puts "End of method"
end
simple_call { puts "Now we are inside the block" }
Output
Start of method
Now we are inside the block
End of method
Passing Arguments via yield
Zero or more expressions may be passed as values to the associated block using the yield
statement. These values are allocated to the block’s parameters, which are specified at the beginning of the block inside vertical bars (|…|
).
Code Example: Passing Arguments
def animals
yield "Tiger"
yield "Giraffe"
end
animals { |x| puts "Hello, #{x}" }
Output
Hello, Tiger
Hello, Giraffe
Code Example: Iterators using yield
Typically, yield
is used in classes to implement methods like times
, each
, and upto
. For instance, a countdown function can continually run the block while providing the current count by using yield
:
def countdown(num)
num.times do |i|
yield(num-i)
end
end
countdown(5) { |i| puts "Call number #{i}" }
Output
Call number 5
Call number 4
Call number 3
Call number 2
Call number 1
Receiving a Value from the Block
The return value of the code block can be recorded via the yield
statement. The block’s value is established by the final expression that is evaluated within it. The yield
expression’s value inside the method is this returned value.
To establish the sorting order or create a new collection, methods such as Array.sort
and Enumerable#collect
use the value returned by the block.
Code Example: Using the Block’s Return Value
def yield_n(n)
p = yield n if block_given?
p || n
end
puts yield_n(12) { |n| n + 7 }
puts yield_n(4)
Output
19
4
Handling Missing Blocks ()
When no block has been linked to the method call, using yield
is an error that causes a LocalJumpError
.
To get around this, the method should check for an attached block before calling yield
using the block_given?
method, which functions similarly to a global function in Ruby.
Code Example: Using block_given?
def block_caller
puts "some code"
if block_given?
yield
else
puts "default"
end
puts "other code"
end
block_caller
Output
some code
default
other code
yield
in Advanced Contexts
Resource Management: yield
is essential for specifying procedures that manage setup and cleanup, guaranteeing that resources (such as files) are released automatically. A block, for example, is frequently used by File.open
; if one is provided, the new File
object is passed to the block, which then automatically shuts the file upon the block’s return.
Enumerators: Iteration methods (like each) should return an Enumerator object if they are called without a block. Should block_given?
be false, the method can either use enum_for
or return self.to_enum(:method_name)
to accomplish this capability.
Block vs. Proc Arguments: A method can explicitly receive an implicit block as a named Proc object by prefixing the final parameter with an ampersand (&
), although yield
is the traditional approach to invoke an implicit block. The yield
statement can be used to invoke the block within the method even if it is recorded as a Proc parameter.