Giving break a return value
Question
What's the return value of the following Ruby code?
(1..10).each { |n| break n if n == 5 } # => ???
The correct answer is
-
nil -
(1..10) -
It raises
LocalJumpError -
5Correct
Explanation
TL;DR
break exits the enclosing iterator immediately, and any argument you give it becomes the return value of the whole method call. When n reaches 5, break n terminates each and makes it return 5. Without the break, each would have returned its receiver, the range itself.
Step by step
The block runs for n = 1, 2, 3, 4 without effect. At n = 5, the condition is true and break n fires: iteration stops (6 through 10 are never yielded) and 5 becomes the value of the each expression.
The variants make the rule visible:
(1..10).each { |n| break n if n == 5 } # => 5
(1..10).each { |n| break if n == 5 } # => nil
(1..10).each { |n| break 42 if n == 5 } # => 42
(1..10).each { |n| n } # => 1..10
A bare break returns nil. break 42 returns 42; the argument is arbitrary, not tied to the block parameter. And with no break at all, each runs to completion and returns the receiver.
The same rule applies to any method that yields, not just each. A break out of map discards the partial result and returns the break value:
(1..10).map { |n| break n if n == 5; n * 2 } # => 5
break vs next
next is the local counterpart: it ends only the current block invocation, and its argument becomes the value of that single yield:
(1..4).map { |n| next 0 if n.odd?; n * 10 } # => [0, 20, 0, 40]
break answers for the whole method call; next answers for one iteration.
The idiomatic spelling
"Stop at the first element matching a condition and return it" already has a name, Enumerable#find:
(1..10).find { |n| n == 5 } # => 5
break with a value earns its place when you need to short-circuit an iterator that find cannot express, such as bailing out of each_slice or a nested loop with a computed result.
Share this quiz
Comments
No comments yet. Be the first.