← Back to Ruby Operators

Curly Braces

Curly braces ({}) are used in Ruby for defining a hash as well as for the shorthand syntax for a block.

Defining a Hash

A hash is a collection of key-value pairs. We can define a hash in Ruby by wrapping curly braces around zero or more key-value pairs.

> {}
=> {}
> { first_name: "Bob", last_name: "Belcher", occupation: "Chef" }
=> {:first_name=>"Bob", :last_name=>"Belcher", :occupation=>"Chef"}
> { [3,4] => 'x', [7,8] => 'y' }
=> {[3, 4]=>"x", [7, 8]=>"y"}

Block Shorthand

Curly braces can be used as an alternative to the do and end keywords, typically when we want to define a single-line block.

> [1,2,3].map { |n| n * n }
=> [1, 4, 9]

There is no reason, besides readability and convention, that we can't do multi-line blocks with curly braces.

user.tap { |user|
  if user.new_record?
    WelcomeEmail.send(user)
  else
    SignInEmail.send(user)
  end
}

If you're about to write something like the above, do consider swapping the curly braces for do/end.

I've never written code where this mattered, but in case it comes up, I'll mention that do/end have lower precedence than {}. Here is an example of where those differences in precedence affect the execution.

def method_one(arg = nil)
  puts "Executing method_one"
  if block_given?
    puts "  (1) method_one got a block"
    yield(1)
  end

  puts "  (1) method_one positional arg: #{arg || "nil"}"

  :method_one
end

def method_two(arg = nil)
  puts "Executing method_two"
  if block_given?
    puts "  (2) method_two got a block"
    yield(2)
  end

  puts "  (2) method_two positional arg: #{arg || "nil"}"

  :method_two
end

puts "Using Curly Braces:"

method_one method_two { |n|
  puts "  (#{n}) Block is executed!"
}

puts "-" * 40

puts "Using do/end:"

method_one method_two do |n|
  puts "  (#{n}) Block is executed!"
end

# Using Curly Braces:
# Executing method_two
#   (2) method_two got a block
#   (2) Block is executed!
#   (2) method_two positional arg: nil
# Executing method_one
#   (1) method_one positional arg: method_two
# ----------------------------------------
# Using do/end:
# Executing method_two
#   (2) method_two positional arg: nil
# Executing method_one
#   (1) method_one got a block
#   (1) Block is executed!
#   (1) method_one positional arg: method_two

In this (contrived) example, we have two methods where either one could be the intended recipient of the block. With the curly brace block, the most immediate method call receives the block argument. Whereas with the do/end block, the outer method receives the block argument. As with most cases of discrepancies in precedence, we are best off using parentheses to remove ambiguity.

References