---
title: Blocks
source: 'https://academia.sh/en/courses/ruby-fundamentals/blocks'
course: 'Ruby Fundamentals'
language: en
updated: '2026-10-21T05:03:42+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Shows that a method calls the block given to it with yield'
    - 'Queries whether a block is present with block_given? and defined?(yield)'
    - 'Measures that the same map message gives two results with two blocks'
    - "Measures that a block's result does not change the return value of each"
---

# Blocks

The same map message is measured giving two different results with two different blocks, a method is shown calling its block with yield, and the block is shown deciding the result without changing the class's return rule.

The previous lesson showed that `Hash.new { 0 }[:x]` and `Hash.new { |_, k| k.to_s }[:x]` give two different results, and that the difference is decided not by the object receiving the message but by the block supplied. In that lesson the block itself was only written. This lesson makes it the subject: a **block** is a piece of code written with curly braces or `do ... end` and given to a method together with a message. A block is not a value by itself; it runs when the method calls it. M10/K03 measured how many times a closure runs in PHP; that question is not asked here, and the question is again who answers.

The course's responders are **class**, **module**, **block** and **syntax**. This lesson's responder is the **block**, but the bounding measurement will show that the block does not decide every result.

## The Same Message, Two Blocks

The measurement below gives the same `map` message on the same array to two different blocks. The array does not change and the message does not change; only the block does. The core classifies the same result too: `reply.rb` counts the responder as a **block** when the two results differ.

```ruby
# reply.rb — helpers that classify who answers a message

# Classification asks in order: is the expression a message at all (defined?),
# then do two different blocks give two different results, then who owns the method.
def responder(definition, owner: nil, result: nil, with_blocks: nil)
  return ["syntax", "defined? #{definition}"] unless definition == "method"

  if with_blocks
    first, second = with_blocks.call
    return ["block", "#{first.inspect} / #{second.inspect}"] unless first == second
  end

  kind = owner.instance_of?(Class) ? "class" : "module"
  [kind, "#{owner}, result #{result.inspect}"]
end
```

```ruby
# block.rb — the same map message with two blocks
require_relative "reply"

numbers = [1, 2, 3]
doubled = numbers.map { |n| n * 2 }
text = numbers.map { |n| n.to_s }

kind, evidence = responder(defined?(numbers.map), with_blocks: -> { [doubled, text] })
puts "#{kind}  #{evidence}"
puts numbers.inspect
```
```
block  [2, 4, 6] / ["1", "2", "3"]
[1, 2, 3]
```

The first line is the core's `element mapping` observation: the **responder** is a **block**. The second line shows that the array did not change; `map` built a new array. **CB3.** The measurement is on an array; on a hash, `map` comes from `Enumerable` and works with the same block logic, but this lesson does not measure it separately.

## How a Method Calls the Block

A method definition can ask whether a block was given when it was called. `block_given?` says this directly; inside the method body, `defined?(yield)` gives the same information: a string on a call with a block, `nil` on a call without one. `yield` calls the given block and passes its arguments to the block's parameters. `yield` is not written at the top level, outside a method; every measurement is inside a method.

```ruby
# yield.rb — a method calls its block with yield
def twice
  puts "with_block=#{block_given?} yield=#{defined?(yield).inspect}"
  return unless block_given?

  yield 1
  yield 2
end

twice
twice { |n| puts "block #{n}" }
```
```
with_block=false yield=nil
with_block=true yield="yield"
block 1
block 2
```

On the call without a block, `block_given?` returned `false`, `defined?(yield)` returned `nil`, and the method exited early. On the call with a block, they were `true` and `"yield"`; the block ran twice, with `1` and `2`. **CB4.** `defined?(yield)` returns `"yield"`, not `"method"`; `yield` is not a method call but a construct of the language itself.

`map` and `each` called without a block raise no error; they return an **iterator (`Enumerator`)**. On a call with a block, `defined?` gives `"expression"`; the core queries the message without a block for this reason, as seen before.

```ruby
# without_block.rb — a call without a block returns an iterator
puts [1, 2, 3].map.class
puts [1, 2, 3].each.class
puts defined?([1, 2, 3].map)
puts defined?([1, 2, 3].map { |n| n })
```
```
Enumerator
Enumerator
method
expression
```

## The Block's Value and Its Surroundings

`yield` is an expression and returns the value of the block's last expression. The method can use this value; `map` does exactly that. A block also sees the local variables of the place where it was defined: this is the **closure** behavior explained in M01/K02, and it is not redefined here, only measured.

```ruby
# value.rb — yield returns the block's value, the block sees its surroundings
def triple
  result = yield 3
  "yield returned: #{result}"
end

factor = 5
puts triple { |n| n * factor }
factor = 7
puts triple { |n| n * factor }

total = 0
[1, 2, 3].each { |n| total += n }
puts "total=#{total}"
```
```
yield returned: 15
yield returned: 21
total=6
```

The block was written the same way in both calls, but it saw the current value of the `factor` variable: first `15`, then `21`. The block did not copy the value when it was defined; it read the variable's current value at the moment it was called. Inside the `each` block, `total` increased the outer variable. **CB6.** This lesson only measures that the block sees the variables of the scope where it was defined; it does not measure other scope rules.

## Two Forms, Many Parameters

Curly braces are preferred for one-line blocks and `do ... end` for multi-line blocks; both build the same block. A block can take several parameters, and the parameters are bound in order to however many values `yield` gives.

```ruby
# forms.rb — two forms build the same block, several parameters
def pair
  yield 1, 2
end

short = pair { |a, b| a + b }
long = pair do |a, b|
  a + b
end
puts "short=#{short} long=#{long}"

{ name: "Alice", age: 30 }.each do |key, value|
  puts "#{key}=#{value}"
end
```
```
short=3 long=3
name=Alice
age=30
```

Both forms gave `3`. The hash's `each` block received the key and the value as two separate parameters. **CB7.** The two forms were measured to give the same result only in these calls; which method a block binds to in a call without parentheses is measured separately in the Which Method It Binds To section.

## Shorthand Forms

Since Ruby 3.4, `it` can be written in a block with a single parameter; `_1` has existed since Ruby 2.7. Both give the same result.

```ruby
# shorthand.rb — it and _1 in a single-parameter block
puts [1, 2].map { it * 3 }.inspect
puts [1, 2].map { _1 * 3 }.inspect
```
```
[3, 6]
[3, 6]
```

## Unpacking Parameters and Order

If a block takes several parameters, a single array passed by the method is distributed across the parameters. `|a, b|` unpacks a pair, and `|a, (b, c)|` unpacks a nested array too. `each_with_index` passes the position along with the element.

```ruby
# unpacking.rb — array unpacking in block parameters
[[1, 2], [3, 4]].each { |a, b| puts "#{a}+#{b}=#{a + b}" }
%w[x y].each_with_index { |letter, index| puts "#{index}:#{letter}" }
[[1, [2, 3]]].each { |a, (b, c)| puts "a=#{a} b=#{b} c=#{c}" }
puts [[1, 2], [3, 4]].map { |pair| pair }.inspect
```
```
1+2=3
3+4=7
0:x
1:y
a=1 b=2 c=3
[[1, 2], [3, 4]]
```

On `[[1, 2], [3, 4]]`, `|a, b|` split each pair into two parameters: `1+2=3` and `3+4=7`. `each_with_index` gave the element and the position as two separate parameters. The parenthesized `(b, c)` unpacked the inner array. The single-parameter `map { |pair| pair }`, on the other hand, took the array whole, without splitting it. **CB23.** Unpacking was measured only on array elements and in these four calls.

## The Block's Outer Local

A block sees the local variables of the place where it was defined and can change them. This is the closure behavior measured above. What happens when a block parameter has the same name as an outer variable is also measured below; the `|x; y|` form makes the name after the semicolon local to the block.

```ruby
# outer_local.rb — a block sees and changes an outer variable
total = 0
[1, 2, 3].each { |n| total += n }
puts total
n = 100
[1, 2].each { |n| n }
puts n
y = 1
[5].each { |x; y| y = x }
puts y
```
```
6
100
1
```

`total` was defined outside the block and the block increased it on every turn: the result is `6`. In the second example, the block parameter `n` had the same name as the outer `n`; the `n` inside the block became a separate variable and the outer `n` stayed `100`. In the third, `|x; y|` declared a `y` local to the block; even though the block wrote `y = x`, the outer `y` stayed `1`. A block can change an outer local; this happens only when the name is not declared local to the block. **CB24.**

## Which Method It Binds To

The two block forms gave the same result at the start of this lesson. The difference appears in a call without parentheses: curly braces bind to the nearest method, while `do` and `end` bound to the outer method in the measurement below. The `ask` method below only reports whether a block was given to it.

```ruby
# binding.rb — which method the block binds to
def ask(*) = "block=#{block_given?}"
puts ask(1) { }
puts ask 1 do end
```
```
block=true
block=false
```

`ask(1) { }` gave the block to `ask` and `block=true` was printed. `puts ask 1 do end` bound the block to `puts`; `ask` received no block and `block=false` was printed. In a call without parentheses, `do` and `end` carry the block to the outer method. **CB25.** Only these two calls were measured; binding in chained calls was not measured separately.

## Returning a Value from a Block

Inside a block, `next` ends the block for that turn and makes the given value the block's value. `break` ends the method itself, and the given value becomes the result of the method call.

```ruby
# next_break.rb — values with next and break
puts [1, 2, 3, 4].map { |n| next 0 if n.even?; n * 10 }.inspect
puts [1, 2, 3].each { |n| break n * 5 if n == 2 }
```
```
[10, 0, 30, 0]
10
```

`next 0 if n.even?` made the block's value `0` on even numbers, and `map` returned `[10, 0, 30, 0]`. Inside `each`, `break n * 5` cut the loop when `n == 2`, and the value of the `each` call became `10`. **CB26.** `next` and `break` were measured only with `map` and `each`.

## A Flexible Number of Parameters

A block does not require the number of parameters to equal the number of values the method passes. Extra values are dropped, and a missing parameter becomes `nil`. When a single array is passed, several parameters unpack it.

```ruby
# flexible.rb — block parameter count versus passed value count
def two
  yield 1, 2
end
two { |a| puts "a=#{a.inspect}" }
two { |a, b, c| puts "c=#{c.inspect}" }
def one
  yield [1, 2]
end
one { |a, b| puts "a=#{a} b=#{b}" }
one { |a| puts "a=#{a.inspect}" }
```
```
a=1
c=nil
a=1 b=2
a=[1, 2]
```

`yield 1, 2` passed two values. The single-parameter block took only the first, the third parameter of the three-parameter block stayed `nil`, and no error occurred. `yield [1, 2]` passed a single array: the two-parameter block unpacked it, and the single-parameter block took the array whole. This looseness is seen in a block and in a proc; the Procs and Lambdas lesson runs the same test with a lambda. **CB39.**

## Using the Block's Value in a Method

`yield` is an expression and its value is the block's last expression. The method can accumulate this value or use it as a condition; in that case, what is done is decided not by the method but by the given block.

```ruby
# yield_value.rb — the value of a yield expression
def transform(list)
  result = []
  list.each { |x| result << yield(x) }
  result
end
puts transform([1, 2, 3]) { |x| x * 2 }.inspect
def count_matching(list) = list.count { |x| yield x }
puts count_matching([1, 2, 3, 4]) { |x| x.even? }
```
```
[2, 4, 6]
2
```

`transform` appended each `yield(x)` value to the result and gave `[2, 4, 6]`. `count_matching` counted the elements for which the block returned true and gave `2`. In both methods the block decided the result; `transform` does not know about doubling and `count_matching` does not know about picking even numbers; both just use the block's value. The responder is therefore the block. **CB40.**

## Block Parameter and Symbol

If a method defines an `&block` parameter, it receives the given block as an object; if no block was given, this parameter is `nil`. The `&:name` form passes a symbol in place of a block.

```ruby
# block_object.rb — the &block parameter and a symbol
def take(&b) = b
puts take.inspect
puts take { }.class
puts %w[a b].map(&:upcase).inspect
puts :upcase.to_proc.call("x")
```
```
nil
Proc
["A", "B"]
X
```

`take` returned `nil` when called without a block and a `Proc` when called with one. `map(&:upcase)` sent the `upcase` message to each element and gave `["A", "B"]`; `:upcase.to_proc.call("x")` returned `X`. A symbol itself is not a block; it turns into a `Proc` with `to_proc`. **CB41.** Proc objects are detailed in the Procs and Lambdas lesson.

## The Bounding Measurement

Does the block always decide the result? The `each` message runs the block for every element, but its return value is not the block's result. Two different blocks give the same return value, and the responder is not the block but the class that owns `each`.

```ruby
# return_value.rb — a block's result does not change the return of each
numbers = [1, 2]
a = numbers.each { |n| n * 10 }
b = numbers.each { |n| n * 99 }
puts a.inspect
puts b.inspect
puts "owner=#{numbers.method(:each).owner}"
```
```
[1, 2]
[1, 2]
owner=Array
```

The two blocks computed different values, but both returned `[1, 2]` from `each`. The return value is decided not by the blocks but by the definition of `each` in the `Array` class. So the block decides the result for `map` and does not for `each`; the same block syntax does two different jobs on two messages. **CB5.** This difference was measured only on these two methods and is not generalized to other iteration methods.

## Summary

- A block is a piece of code given to a method together with a message; the method calls it with `yield`.
- This lesson's responder is the **block**: the same `map` message gave `[2, 4, 6]` with `{ |n| n * 2 }` and `["1", "2", "3"]` with `{ |n| n.to_s }`.
- `block_given?` and `defined?(yield)` query whether a block is present; `map` and `each` without a block return an iterator.
- `it` is available in a single-parameter block since Ruby 3.4, and `_1` since Ruby 2.7.
- The bounding measurement: `each` returns `[1, 2]` with both of two different blocks; in this case the `Array` class decides the return value.

## Next Step

`map` was `Array`'s method on an array and `Enumerable`'s method on a hash. The next lesson looks at that module itself: where does a method that the array does not have itself come from, and how do you give your own class the same ability with a single `each`.
