DEV Community

Cover image for About Ruby Block Syntax
Kenta Takeuchi
Kenta Takeuchi

Posted on • Originally published at bmf-tech.com

About Ruby Block Syntax

This article was originally published on bmf-tech.com.

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}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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!
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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]
Enter fullscreen mode Exit fullscreen mode

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]
Enter fullscreen mode Exit fullscreen mode

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

  • Use do..end for multiple lines
  • Use {} for single lines
  • Use {} when used as a return value
  • Use do..end when passing procedures

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

References

Top comments (0)