In Ruby, both procs and blocks have a similar purpose — i.e. they allow you to group and execute lines of code later on. However, they differ in terms of the following:
Type
Procs and blocks are of a different type:
- A
Proc
is an object of theProc
class that encapsulates a block of code:my_proc = Proc.new { puts "Hello, World!" } # ...
- A block is a language construct (i.e. part of the language syntax); it is a piece of code written between
do
andend
or enclosed by curly braces ({}
):# ... my_method { | num | num * 2 }
Functionality
Procs and blocks are functionally different:
- Procs are objects that can be stored in variables, passed as arguments to methods, and returned as values:
# procs can be passed as arguments to methods def exec_proc(proc) proc.call end # procs can be stored in variables my_proc = Proc.new { puts "Hello World!" } exec_proc(my_proc) #=> "Hello World!"
# procs can be returned as values from methods def create_proc Proc.new { puts "Hello World!" } end my_proc = create_proc my_proc.call #=> "Hello World!"
- Blocks are not standalone objects and can only be directly passed to methods for immediate invocation:
# using a block for immediate invocation puts 2.times { puts "Hello!" } #=> "Hello!Hello!"
# passing a block to a method for immediate invocation def exec_block yield end exec_block { puts "Hello World!" } #=> "Hello World!"
Way of Invocation
Procs and blocks are invoked in different ways:
- Procs can be explicitly invoked using the
Proc#call
method:my_proc = Proc.new { | name | puts "Hello, #{name}!" } my_proc.call("John") #=> Hello, John!
- Blocks are implicitly invoked by using them with methods that
yield
control to them:def exec_block yield("John") end exec_block { | name | puts "Hello, #{name}!" } #=> "Hello, John!"
Accepting Arguments
Procs can accept arguments and parameters, whereas blocks cannot:
- When defining a
Proc
, you can specify parameters within its block argument list:my_proc = Proc.new { | x, y | puts "Sum: #{x + y}" } my_proc.call(3, 4) #=> "Sum: 7"
- Blocks don't have explicit parameter definitions; instead they receive arguments from the method they are associated with:
def calculate yield(3, 4) end # block without explicit parameters, receives arguments from the method calculate { | x, y | puts "Sum: #{x + y}" } #=> "Sum: 7"
Return Behavior
Blocks implicitly return their value to the calling method, while procs have explicit return statements:
- Procs can use the
return
keyword to terminate execution and return a value:def find_first_even_num(numbers) proc = Proc.new do |num| return num if num.even? end numbers.each(&proc) end result = find_first_even_num([1, 3, 4, 5, 6]) puts result #=> 4
- Blocks' result is passed back to the calling method when they complete execution:
def find_first_even_num(numbers) numbers.each do | num | next if num.odd? yield num break end end find_first_even_num([1, 3, 4, 5, 6]) do | result | puts result #=> 4 end
This post was published by Daniyal Hamid. Daniyal currently works as the Head of Engineering in Germany and has 20+ years of experience in software engineering, design and marketing. Please show your love and support by sharing this post.