Ruby has implicit returns. This means that if a return
is the last expression in a path of execution, there's no need for the return
keyword.
Worth noting is that return
's default argument is nil
, which is a falsey value.
def foo
do_stuff
return
end
def bar
return do_more_stuff
end
is equivalent to:
def foo
do_stuff
nil
end
def bar
do_more_stuff
end
Depending on what foo
is doing, returning nil may or may not actually be necessary.
def foo
return raise SomeException
do_something
end
This is equivalent to:
def foo
return(raise(SomeException))
do_something
end
which means it calls raise() first. raise() immediately changes the path of execution, and thus the return is never ran -- it's effectively dead code.
If there is an unconditional raise() followed immediately by an unconditional return(), it's unnecessary for the same reasons as above.
def bar
if some_condition
raise SomeException
return
end
end
In this case, both the raise() and return() calls are conditional, so they are both required.
def baz
if condition_one
raise SomeException if condition_two
return unless condition_three
# <actual useful code here>
end
end
Time for a slight diversion for something that caught my eye in the last section.
return unless X
/return if X
/raise unless X
/raise if X
is sometimes referred to as the "guard pattern."
It serves two purposes, and in many cases can make code considerably easier to reason about (for me, anyway):
- Clustering lists of terminating conditions
- Reducing levels of indentation
def asdf
if condition_one
# code
else
raise SomeException
end
end
def fdsa
if condition_one
# Code blob #1
else
return
end
# Code blob #2
end
The above two functions can become this:
def asdf
raise SomeException unless condition_one
# code
end
def fdsa
return unless condition_one
# Code blob #1
# Code blob #2
end