Application 2024-05-15

About Ruby Block Syntax

Learn Ruby block syntax with do..end and {} forms, passing blocks to methods, yield statements, and Proc/lambda closure behavior.

Read in: ja
About Ruby Block Syntax

Overview

This post discusses Ruby's block syntax.

What is Block Syntax?

A syntax that allows you to pass code snippets to other methods, enabling those methods to execute the code within.

Defined using do..end or {}. Blocks can accept arguments.

# do..end
[1...3].each do |number|
  puts number
end

# {}
[1...3].each { |number| puts number}

You can also define methods that accept blocks.

def greet
  puts "Hi"
  # Execute the passed block
  yield if block_given? # block_given? checks if a block was passed
  puts "Bye"
end

# Passing a block

greet do
  puts "Hello"
end

# Output:
# Hi
# Hello
# Bye

Additionally, you can pass blocks as method arguments using &.

def greet(&block)
  puts "Hi"
  block.call if block
  puts "Bye"
end

greet do
  puts "Hello"
end

# Output:
# Hi
# Hello
# Bye

Objects with a to_proc method can also be passed as blocks.

class Greeting
  def to_proc
    Proc.new { |name| puts "Hello, #{name}!" }
  end
end

%w(Alice Bob Charlie).each(&Greeting.new)

# Output:
# Hello, Alice!
# Hello, Bob!
# Hello, Charlie!

Blocks are closures, just like Proc and lambda.

def count(&block)
  puts block.call
  return block
end

x = 0
b = count do
  x + 1
end # =>1

x += 10
puts b.call # =>11

do..end and {} have different precedence, with {} having higher precedence.

array = [1, 2, 3]

# Low precedence, so the block is evaluated before being passed to map (it gets passed to pp instead)
pp array.map do |x|
  x * 2
end

# Output:
#<Enumerator: ...>

# High precedence, so the block is passed to map and evaluated
pp array.map { |x| x * 2 } # => [2, 4, 6]

You can replace a block with a symbol only when calling a single method within the block. Symbols have the to_proc method (Symbol#to_proc) implemented.

# Normal form
numbers = [1, 2, 3, 4, 5]
even_numbers = numbers.select { |n| n.even? }
p even_numbers.inspect # => [2, 4]

# Using a symbol
numbers = [1, 2, 3, 4, 5]
even_numbers = numbers.select(&:even?)
p even_numbers.inspect # => [2, 4]

# Using a symbol with a custom method
module Numbers
  def my_even?
    self % 2 == 0
  end
end

class Integer
  include Numbers
end

puts [1, 2, 3].select(&:my_even?)  # => [2]

As for the distinction between do..end and {}, consider the following:

Since this can also depend on coding conventions, it's best to consider this as just one example.

References

Tags: Ruby
Share: 𝕏 Post Facebook Hatena
✏️ View source / Discuss on GitHub
☕ Support

If you enjoy this blog, consider supporting it. Every bit helps keep it running!


Related Articles