This post was originally published on the New Bamboo blog, before New Bamboo joined thoughtbot in London.
We are currently hiring at New Bamboo. As part of the process, we send candidates a code test to complete at their own leisure. I won’t reveal any details here, but it’s nothing special.
Nevertheless, I have personally reviewed a bunch of these code tests, and there’s something that has caught my attention. A number of applicants misunderstand the usage of the inline
rescue construct in Ruby. Not a whole lot of them, but enough that I felt compelled to write this piece in order to help other people out there.
What I am seeing is code like the following:
do_something rescue SomeException
I assume applicants read that as “if
SomeException is raised, rescue it and just carry on as if nothing had happened”. Unfortunately, this is not the case. I’ll show with a different example. See this:
do_something rescue nil
What the above does is:
- It calls the method
- If that raised an exception, it’s rescued immediately. The line evaluates to
- If no exception was raised, the line evaluates to the return value of
The following is equivalent:
begin do_something rescue nil end
Five lines with their indentation against a clever one-liner. Tempting! If you know that
do_something may raise an exception, you use an inline
rescue to quickly stop that and continue. Isn’t that neat?
Except that it’s a headache waiting to happen. Consider this code:
def someting_is_wrong? rand < 0.01 # Good 99% of the time end def do_something if something_is_wrong? raise MyIgnorableException else "foo" end end begin do_something rescue nil end
This code looks ok. However, when we run it, something is amiss. It always returns
nil. We expected it to fail (return
nil) only in 1% percent of the cases, but it’s actually failing 100% of the time. What’s going on?
Well, that we misspelled the name of the first method, that’s what. Read again:
someting_is_wrong?. An “h” is missing.
We are calling a method that doesn’t exist. This raises a
NoMethodError, which bubbles up to the
rescue, which in turn ends up rescuing more than we wanted. Remember that an unqualified
rescue will catch any exceptions that inherit from
StandardError. That is dangerous, as it is likely that there will be other problems.
Instead we should use the following invocation:
begin do_something rescue MyIgnorableException nil end
This time we get a
NoMethodError straight away. We notice the problem, fix it, and continue on our merry way, safe in the knowledge that we’ll be notified of any unforeseen problems.
In summary, there are two problems with inline
rescue of exceptions:
- The syntax can be misleading, making you think that it’s rescuing only a specific type of exception, when it’s not. It’s rescuing any type of exception (as long as it inherits from
StandardError), then evaluating the expression that follows it
- Rescuing exceptions without sensible filtering will make you miss other problems that you didn’t expect
Therefore, here are two pieces of advice:
- Never use the unfiltered version of
- Never use the inline
rescue, as it’s effectively an unfiltered
OK, I say “never”, but Avdi Grimm could come up with a decent use case for the inline
rescue. Rather than outlining it here, stealing Avdi’s thunder, and making this longer than it needs to be, I’m just going to link to the screencast where he explains it better than I can, in just 3:17 minutes. It’s issue #22 of his excellent Ruby Tapas, and it’s available for free at his blog: Ruby Tapas #22 - Inline Rescue.