In Ruby, how does a method run the block it was called with using yield, and what does block_given? protect against?
answer
- one implicit block per call
- arguments flow into block parameters
- yield evaluates to the block's value
- LocalJumpError: no block given (yield)
- block_given? returns true or false
basics
~20 syield runs the block attached to the current method call, passes its arguments to the block's parameters and evaluates to the block's last value. With no block, yield raises LocalJumpError; block_given? lets the method check first.
solid answer
~40 sEvery Ruby method call can carry one block, written with `{ }` or `do ... end`, even though the method's parameter list never mentions it. Inside the method, `yield` runs that block: `yield a, b` hands values to the block's parameters, and the `yield` expression evaluates to the value of the block's last expression. A method may yield zero times, once, or many times. If the caller passed no block, `yield` raises `LocalJumpError` with the message `no block given (yield)`. `block_given?` returns `true` or `false`, so a method can make its block optional: yield when one is present and fall back to a default otherwise. `iterator?` is a deprecated alias; with deprecation warnings on it warns and points to `block_given?`.
code
ruby · 16 linesdef each_pair_of(items)
return :no_block unless block_given?
results = []
items.each_slice(2) { |a, b| results << yield(a, b) }
results
end
each_pair_of([1, 2, 3, 4]) { |a, b| a + b } # => [3, 7]
each_pair_of([1, 2]) # => :no_block
def strict
yield
end
strict # LocalJumpError: no block given (yield)go deeper
Recall that yield runs the caller's block, passes arguments into its parameters and evaluates to the block's last value, and that block_given? guards against LocalJumpError.
Explain that the block is an implicit, optional part of every call, that yield inside a nested block still targets the method's block, and when yield is rejected at parse time.
Show how you design APIs where the block is optional, choosing a sensible fallback when block_given? is false, and why yield keeps the block anonymous and cheap for simple callbacks.
Weigh block-based APIs against passing explicit callable objects for team code: blocks read naturally but allow only one per call, which shapes how extensible an interface is.
## The implicit block In Ruby, a **block** is a chunk of code attached to a method call, written between `{ }` or `do ... end`. It is not an argument in the parameter list: any method can receive one, and a method that never mentions a block simply ignores it. ```ruby def repeat(times) count = 0 while count < times yield count count += 1 end times end repeat(3) { |i| puts "attempt #{i}" } ``` - The block `{ |i| puts ... }` belongs to the call `repeat(3)`. - Each `yield count` runs the block once, binding `count` to the block parameter `i`. - `repeat` itself returns `times`; the block's values are available to the method but not returned automatically. ## yield passes values in and gets a value back `yield` behaves like calling an anonymous function that the caller supplied: 1. **Arguments** after `yield` are passed to the block's parameters: `yield key, value` fills `|key, value|`. 2. The block runs in the **caller's scope**, so it can read the caller's local variables. 3. The whole `yield` expression **evaluates to the block's last expression**, so a method can use the result: `total += yield(item)`. That third point is what makes blocks useful for transforming, filtering and wrapping work: the method decides *when* and *how often* to run the code, and the block decides *what* it computes. ## What happens without a block If a method uses `yield` and the caller gives no block, Ruby raises **`LocalJumpError`** with the message `no block given (yield)`. The error is raised at the `yield`, not when the method is called, so any code before it has already run. | Situation | Result | |---|---| | Block given, `yield` runs | the block's last value | | No block, `yield` runs | `LocalJumpError: no block given (yield)` | | No block, `block_given?` | `false` | | Block given, `block_given?` | `true` | ## block_given? makes the block optional **`block_given?`** is a `Kernel` method that reports whether the current method call received a block. It is the standard guard for methods whose block is optional: ```ruby def fetch_setting(name) value = ENV[name] return value if value block_given? ? yield(name) : nil end fetch_setting("LOG_LEVEL") { |key| "info" } ``` Points worth knowing: - **`iterator?`** is a deprecated alias of `block_given?`; calling it emits a deprecation warning that names `block_given?` as the replacement, when deprecation warnings are enabled. - `defined?(yield)` returns `"yield"` when a block is present and `nil` otherwise; core Ruby code uses it, but `block_given?` reads more clearly in application code. - You do **not** need an `&block` parameter to use `yield`. Declaring one captures the block as an object, which is a separate technique. ## Where yield is allowed `yield` belongs to method bodies: - Inside a block that sits in a method body, `yield` still targets the **method's** block. That is how `items.each { |item| yield item }` hands each element to the caller's block. - At the top level of a script, directly in a class body, or in a top-level lambda, `yield` is rejected when the file is parsed with a `SyntaxError` reporting `Invalid yield`. ## Common mistakes - **Assuming the block is an argument.** It is not counted in the method's arity, so a missing block never raises `ArgumentError`; the failure appears only when `yield` runs. - **Checking too late.** If the method writes to a file or a database before reaching `yield`, a blockless call leaves half-done work behind; check `block_given?` at the top. - **Ignoring the block's value.** Methods such as a custom `sum_by` exist precisely to use what `yield` returns; discarding it and returning something else surprises callers. - **Expecting an error for an unused block.** Passing a block to a method that never uses it is legal; Ruby ignores the block, and since Ruby 3.4 only `-w` can warn about it for Ruby-defined methods. ## Interview angle A strong answer covers the three moving parts: the block is implicit and optional, `yield` both sends arguments and receives the block's value, and calling `yield` without a block raises `LocalJumpError`, which `block_given?` prevents. Mentioning that `iterator?` is deprecated shows familiarity with older code bases.
- Inside a method, what does yield target when it appears within a block passed to another method, such as items.each { |i| yield i }?It targets the block given to the enclosing method, not the inner block. The inner block is a closure over the method's frame, so `yield i` runs the caller's block once per element. This is the usual way to build your own iterator on top of `each`.
- Is there a way besides block_given? to detect a block, and which should you prefer?`defined?(yield)` returns `"yield"` when a block is present and `nil` otherwise, and some core methods use it. `iterator?` also works but is deprecated and warns when deprecation warnings are on. In application code `block_given?` is the idiomatic, readable choice.
saying these in an interview costs you the question
- A method must declare an &block parameter before it can call yield
- yield without a block silently returns nil and the method carries on
- yield always returns nil, so the block's result is lost
- iterator? is the modern replacement for block_given?
- A method can call yield only once per invocation