Summary
- Use
return
to return from a method.return
accepts a value that will be the return value of the method call. - Use
break
to quit from a block and from the method that yielded to the block.break
accepts a value that supplies the result of the expression it is “breaking” out of. - Use
next
to skip the rest of the current iteration.next
accepts an argument that will be the result of that block iteration.
The following method will serve as an example in the details below:
def example
puts yield
puts 'done'
return 'example'
end
# Intended usage & output:
example { 'hallo welt' }
# hallo welt
# done
# => 'example'
Return within a block
Return is only valid inside a method. If you use it inside a block or not is not relevant. Return lets you jump out of a method and returns nil
or an argument.
return # => LocalJumpError: unexpected return
example { return 4 } # => LocalJumpError: unexpected return
def test
example { return 4 }
return 'test'
end
test # => 4
Note how test
returns the return
value from the block; neither code after the example
invocation (returning "test") nor code after the yield
inside example
(putsing "done", returning "example") are executed. As you always knew, and in blocks too: return
exits that method NOW.
If you nest blocks return
is still jumping out of the method (and not out of the first block or something similar).
def test
[1, 2, 3].each do |counter|
example { return counter }
end
return 'test'
end
test # => 1
Conclusion: return
has no special meaning in blocks, but it can be misunderstood as "return from block", which is wrong. See Fun with Ruby: Returning in blocks "overwrites" outside return values for an example.
Break within a block
Break is only valid within a block. It lets you jump out of a block and returns nil
or the provided argument to the caller.
break # => SyntaxError: Can't escape from eval with break
example { break 'break' }
# => 'break'
Note how break
changes the return value of the method yielding to the block from example
to its argument. Also, the code after the yield
in example
is not executed! Probably, this behavior was designed to enable programmers writing their own iterators (like while
or loop
) as methods and still get all the keyword love from Ruby.
As a side note, using break
also could indicate a code smell (when we look at what was said above about the expected return value):
# bad
[1, 2, 3].each { |counter| break counter if counter.even? } # => 2
[1, 3].each { |counter| break counter if counter.even? } # => [1, 3]
# good
[1, 2, 3].find(&:even?) # => 2
[1, 3].find(&:even?) # => nil
Next within a block
Use next
to skip the rest of the current iteration. next
accepts an argument that can be used as the result of the current block iteration.
next # => SyntaxError: (irb):24: Can't escape from eval with next
example { next 'next' }
'next'
'done'
# => 'example'
Note how next
exits the block returning its argument as block return value, but the example
method still gets to continue with its code after the yield
.
A real world example could be logging on user creation without changing the return value:
def save_user
user.save.tap do |saved|
next unless saved
Rails.log("User was created, we have #{user.count} users now!")
end
end
save_user
# User was created, we have #{user.count} users now!
# => true
Other rules apply to lambdas
A lambda (lambda { ... }
or -> { ... }
) cannot return from the containing function.