Ruby do Keyword
last modified October 18, 2026
This tutorial explains how to use Ruby's do keyword to create
blocks. Blocks are closures that can be passed to methods and can capture
variables from the surrounding scope.
The do keyword starts a block in Ruby. It pairs with end
to delimit the block's body. Blocks can accept parameters.
Ruby blocks come in two forms: do...end and {...}.
The do form is commonly used for multi-line blocks and is preferred
for readability with longer blocks; braces are common for short, single-line
blocks.
Basic do Block with Array#each
This example shows the simplest use of do with the each
method. The block executes for each array element.
fruits = ["apples", "bananas", "cherries"]
fruits.each do |fruit|
puts "I love #{fruit}!"
end
The do keyword starts the block, with |fruit| as the
parameter. The block runs three times, once for each array element.
do Block with Multiple Parameters
Blocks can accept multiple parameters. This example uses each_with_index
which provides both value and index.
colors = ["red", "green", "blue"]
colors.each_with_index do |color, index|
puts "Color #{index + 1}: #{color}"
end
The block receives both the array element and its index. Parameters are separated by commas within the pipes. This prints a numbered color list.
Custom Method with do Block
Methods can accept blocks using yield. This example shows how to
create a method that takes a block.
def repeat(times)
times.times do |i|
yield(i + 1)
end
end
repeat(3) do |count|
puts "Iteration #{count}"
end
The repeat method yields to its block the specified number of
times. The block receives the current iteration count as a parameter.
do Block with Hash Iteration
Hashes can be iterated with each, yielding key-value pairs. The
do block makes this readable.
person = { name: "Alice", age: 30, occupation: "Developer" }
person.each do |key, value|
puts "#{key.capitalize}: #{value}"
end
The block receives two parameters for each hash pair. The do form
is ideal for multi-line hash processing like this example.
do Block with File Operations
File operations often use blocks for automatic resource management. This example
shows File.open with a block.
File.open("data.txt", "w") do |file|
file.puts "Line 1"
file.puts "Line 2"
file.puts "Line 3"
end
The file automatically closes after the block executes. This pattern ensures proper resource cleanup even if errors occur within the block.
Nested do Blocks
Blocks can be nested for multi-dimensional iteration. This example shows nested
each calls with do blocks.
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
matrix.each do |row|
row.each do |cell|
print "#{cell} "
end
puts
end
The outer block processes each row, while the inner block handles individual
cells. The do form clearly shows the nested structure.
Block Precedence: do...end vs {...}
The two block forms have different precedence. Braces bind tightly to the
nearest method call, while do...end binds less tightly. This
matters when a method call has arguments and no parentheses.
def show(value)
puts value.is_a?(Enumerator) ? "got an Enumerator" : value.inspect
end
show [1, 2, 3].map { |x| x * 2 }
show [1, 2, 3].map do |x|
x * 2
end
The braces bind to map, so the first call passes
[2, 4, 6] to show. The do...end block
binds to show instead, leaving map without a block;
it returns an Enumerator. Use braces or add parentheses around the
inner call when precedence matters.
do Block with begin/rescue
Blocks can contain their own exception handling. This example puts a
begin/rescue inside the block so each iteration can handle its own
error.
[50, 0, 10].each do |divisor|
begin
puts "100 / #{divisor} = #{100 / divisor}"
rescue ZeroDivisionError
puts "Cannot divide by zero!"
end
end
Each iteration runs its own begin/rescue. The division by zero is
rescued inside the block, so the loop continues instead of terminating the
program.
Source
This tutorial covered Ruby's do keyword with practical examples
showing iteration, method blocks, file operations, block precedence, and
error handling.
Author
List all Ruby tutorials.