LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 822 min read

Blocks, Procs & Lambdas

Master Ruby blocks, the yield keyword, Proc objects, and lambdas — and understand the real differences between procs and lambdas.

Introduction

Blocks, procs, and lambdas are Ruby's answer to "code as a value" — the same underlying idea as closures in Lua or JavaScript, expressed through three closely related but distinct Ruby constructs. You've already used blocks throughout this course (in .each, for instance) without a full explanation; this lesson gives you one.

What You Will Learn
  • What a block is, and its two equivalent syntaxes ({ } and do...end).
  • How yield calls the block passed to a method.
  • What Proc and lambda objects are, and how to create each.
  • The real, practical differences between procs and lambdas.

Blocks: { } and do...end

A block is a chunk of code attached to a method call — you've already used one every time you called .each. Ruby offers two interchangeable syntaxes: curly braces for short, single-line blocks, and do...end for longer, multi-line ones.

[1, 2, 3].each { |n| puts n * 2 }
[1, 2, 3].each do |n|
doubled = n * 2
puts doubled
end
Output

Click Run to see what this code prints.

yield: Calling a Block from a Method

Use case: inside any method, yield calls whatever block was passed to it at the call site — this is how methods like .each are actually implemented under the hood, and you can write your own methods that accept blocks the exact same way.

def repeat(times)
times.times do |i|
yield i
end
end
repeat(3) { |i| puts "Iteration #{i}" }
Output

Click Run to see what this code prints.

Procs: Blocks as Objects

Use case: a Proc turns a block into a real object you can store in a variable, pass around, and call later — useful when a chunk of code needs to outlive a single method call, or be reused in multiple places.

double = Proc.new { |n| n * 2 }
puts double.call(5) # 10
puts double.(5) # 10 -- shorthand call syntax
puts double[5] # 10 -- another equivalent shorthand
Output

Click Run to see what this code prints.

Lambdas

A lambda is a special kind of Proc, created with lambda { ... } or the shorter -> (...) { ... } "stabby lambda" syntax — functionally very close to a Proc, but with two important behavioral differences covered next.

square = ->(n) { n * n }
puts square.call(6) # 36
puts square.(6) # 36
Output

Click Run to see what this code prints.

Procs vs Lambdas: The Real Differences

AspectProcLambda
Argument count checkingLenient — extra or missing arguments are silently ignored/nilStrict — calling with the wrong number of arguments raises an error
return keyword inside itReturns from the enclosing method entirelyReturns only from the lambda itself, like a normal method
Creation syntaxProc.new { ... }lambda { ... } or ->(args) { ... }
def proc_test
my_proc = Proc.new { return "from the proc" }
my_proc.call
"This line never runs"
end
def lambda_test
my_lambda = lambda { return "from the lambda" }
my_lambda.call
"This line DOES run"
end
puts proc_test
puts lambda_test
Output

Click Run to see what this code prints.

Why This Distinction Matters

A proc's return exits the entire enclosing method immediately, which can be surprising if you weren't expecting it. A lambda behaves like a normal method call — its return only exits the lambda itself. This is the single most important practical difference between the two.

Common Mistakes

Avoid These Mistakes
  • Using return inside a Proc without realizing it exits the entire enclosing method, not just the proc.
  • Assuming a Proc and a lambda are fully interchangeable — their argument-checking strictness and return behavior genuinely differ.
  • Forgetting yield requires a block to actually be passed at the call site, unless you guard it with block_given?.

Best Practices

  • Default to lambdas over Proc.new for most standalone reusable code blocks, since their stricter, more predictable behavior avoids surprises.
  • Use { } for short, single-expression blocks, and do...end for longer, multi-line ones — a widely followed style convention.
  • Check block_given? inside a method before calling yield if the block is genuinely optional.

Frequently Asked Questions

Blocks, procs, and lambdas are all closures in the general sense — they capture and remember variables from their surrounding scope, the same underlying concept covered for Lua elsewhere on this platform.

Yes — prefixing a parameter with & (like def run(&block)) captures the passed block as an explicit Proc object you can store, pass along, or call with .call, instead of relying on the implicit yield.

A block is lightweight and implicit — perfect for the extremely common case of a method taking exactly one block. A Proc is that same idea promoted to a full, storable, reusable object once you need more flexibility than a single inline block offers.

Key Takeaways

  • A block is a chunk of code attached to a method call, written with { } or do...end.
  • yield calls the block passed to the current method.
  • A Proc turns a block into a reusable, storable object; a lambda is a stricter variant of a Proc.
  • Lambdas check argument counts strictly and return only from themselves; Procs are lenient and return exits the whole enclosing method.

Summary

Blocks, procs, and lambdas are a big part of what makes Ruby code feel so expressive — passing behavior around as naturally as passing any other value. Next, you'll cover classes and modules, Ruby's tools for organizing and sharing object-oriented code.

Next Lesson →

Classes & Modules