100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Ruby

Blocks, Procs, and Lambdas

Explore Ruby's three flavors of closures -- blocks, Procs, and lambdas -- and how they differ in argument strictness, return semantics, and reusability.

Methods & BlocksIntermediate10 min readJul 9, 2026
Analogies

Blocks, Procs, and Lambdas

Ruby treats chunks of code as first-class values through three closely related mechanisms: blocks, Procs, and lambdas. All three are closures -- they capture the local variables and self from the scope in which they were created -- but they differ in how they're passed around, how strict they are about argument counts, and what return does inside them. A block is the anonymous chunk of code attached to a method call with do...end or curly braces; a Proc is an object wrapping a block of code that can be stored in a variable and passed around; a lambda is a special kind of Proc with method-like argument checking and return behavior. Mastering the distinctions is key to writing idiomatic Ruby, since blocks power nearly every Enumerable method.

🏏

Cricket analogy: Blocks, Procs, and lambdas are like three ways a substitute fielder can be briefed: a block is a quick shouted instruction from the captain for one play, a Proc is a written note the twelfth man can carry and reuse across overs, and a lambda is a formal signed instruction sheet with strict rules about what it covers.

Blocks as implicit code arguments

Every Ruby method can optionally receive a block, even if its definition doesn't mention one explicitly. The block isn't a regular argument; it's passed implicitly and captured inside the method with yield or by converting it to a Proc with an explicit &block parameter. Blocks are the most common way Ruby code expresses "do this operation, but let the caller customize a step," which is why each, map, select, and friends all read so naturally.

🏏

Cricket analogy: Just as a coach's shouted field-placement instruction is implicitly understood without being written into the team sheet, a Ruby method can implicitly accept a block without declaring it, capturing it with yield or an explicit &block parameter the way a captain might explicitly note a specific fielding plan.

ruby
[1, 2, 3].each { |n| puts n * 2 }

[1, 2, 3].each do |n|
  puts n * 2
end

def repeat(times)
  return enum_for(:repeat, times) unless block_given?
  times.times { |i| yield i }
end

repeat(3) { |i| puts "iteration #{i}" }

Procs: flexible, storable closures

A Proc is created with Proc.new { ... } or the proc { ... } shorthand, and it can be stored in a variable, passed to methods, and called later with .call, .(), or []. Procs are lenient about arity: calling a Proc with too few or too many arguments does not raise an error -- missing arguments become nil and extras are silently discarded, much like how JavaScript functions historically ignored extra arguments.

🏏

Cricket analogy: A Proc is like a substitute umpire's written checklist that can be handed off and reused across matches; call it with too few checks (.call) and it silently skips them, call it with extras and it just ignores the rest, much like a lenient scorer overlooking incomplete paperwork.

ruby
adder = Proc.new { |a, b| (a || 0) + (b || 0) }
adder.call(1, 2)      # => 3
adder.call(1)         # => 1  (b is nil, treated as 0)
adder.call(1, 2, 3)   # => 3  (extra arg 3 ignored)

Lambdas: strict arity and local return

A lambda, created with lambda { ... } or the stabby-lambda syntax ->(a, b) { ... }, behaves much more like a regular method. It enforces strict arity -- calling it with the wrong number of arguments raises an ArgumentError -- and a return inside a lambda only exits the lambda itself, returning control to the calling code, rather than attempting to return from the enclosing method. This local-return behavior is what makes lambdas safe to use deep inside other methods without unexpected control-flow jumps.

🏏

Cricket analogy: A lambda is like a strict DRS review protocol: calling it with the wrong number of camera angles raises an error immediately, and a return inside it only ends that single review, handing control straight back to the umpire rather than aborting the whole match.

ruby
square = ->(n) { n * n }
square.call(4)   # => 16
square.(4)       # => 16
square[4]        # => 16

safe_divide = lambda do |a, b|
  return Float::INFINITY if b.zero?
  a / b.to_f
end
safe_divide.call(10, 0)   # => Infinity, and execution continues normally afterward

A return inside a Proc (not a lambda) attempts to return from the enclosing method that created the Proc, not just the Proc itself. If that enclosing method has already finished executing by the time the Proc is called, Ruby raises a LocalJumpError. This is one of the most confusing gotchas for developers moving from lambdas to Procs, or from other languages where closures never behave this way.

You can convert a block into a Proc using &block in a method signature, and convert a Proc back into a block-like argument at a call site using &my_proc. Symbols also respond to to_proc, which is why ["a", "b"].map(&:upcase) works: &:upcase calls :upcase.to_proc and passes the resulting Proc as a block.

  • Blocks are anonymous, implicit code arguments attached with do...end or { }; only one can be passed per method call.
  • Procs are objects wrapping a block; they can be stored, passed around, and are lenient about argument count (arity).
  • Lambdas are Procs created via lambda or ->(); they enforce strict arity and check argument counts like methods do.
  • return inside a lambda exits only the lambda; return inside a Proc attempts to return from the enclosing method.
  • &block converts a block to a Proc parameter; &:symbol leverages Symbol#to_proc for concise transformations.
  • Blocks/Procs/lambdas all close over local variables and self from their defining scope, making them true closures.

Practice what you learned

Was this page helpful?

Topics covered

#Ruby#RubyStudyNotes#Programming#BlocksProcsAndLambdas#Blocks#Procs#Lambdas#Implicit#StudyNotes#SkillVeris