Ruby 'Exceptional' Knowledge
Most of us have encountered the methods
break in Ruby. We can generally understand what’s going on when we read code that uses it, but what exactly is the difference between them all? Here’s a quick guide plus some fun and useful facts.
If you remember just one thing…
rescueare used for handling errors only
catchare used in order to terminate execution early when no other work is needed. source
Raise and Rescue
rescue are used exclusively for handling errors. By default, raising an error will exit the program.
1 2 3 4 5 6 7 8 9
This happens unless there is a
rescue statement which will run in case of an exception.
1 2 3 4 5 6 7 8 9 10
The above Ruby code can be rewritten like so:
1 2 3 4 5 6 7 8 9 10
Ruby has many different types of exceptions (see documentation). Raise takes up to three parameters: * the exception type * an error message * an array of callback information.
All three are optional and Ruby knows that if you only pass in a string that it’s the message. Usually you don’t set the last parameter since
Kernel#caller automatically creates that array.
Here are a couple of valid
Exception is the root of Ruby’s exception hierarchy. It’s the class from which all Exceptions descend. It is king. This has a very interesting consequence.
rescue Exception rescues from EVERYTHING, including syntax errors, load errors, and any of the following listed below.
Interruptprevents you from being able to CTRL+C out of the program.
SignalExceptionprevents the program from responding correctly to signals. It will be unkillable, except with kill -9. source
1 2 3 4 5 6 7
Break, Catch, and Throw
throw are used in order to terminate execution early when no other work is needed.
break leaves the current loop while the
throw combination can be used to break out of any number of loops at one time.
1 2 3 4 5 6
1 2 3 4 5 6 7 8 9 10 11 12
Notice that the two loops are enclosed in the
catch block. This means that once the
throw statement is executed, it will store the value of its second argument into
:recipe and send it back to the catch statement. By doing so, it exits all the loops after finding the first recipe match. From there, the method finishes executing as normal.
Because my example is a bit contrived, I will post a real-life example from another blog by rubyist Avdi Grimm.
1 2 3 4 5 6 7 8 9 10 11 12 13
Since loading pages over and over again can be an expensive process, the coder above uses a
catch to exit the loop when the first matching result is found.
Throw, Catch and Sinatra
An even more mind-blowing example from the same blog post reveals that Sinatra has a built-in catch for the
#last-modified method. You might use this method to check a user’s cache for what version of a certain page the user has on his/her machine. Why would you do this? Simple! In order to cut out any expensive and unnecessary processing. If the page in the cache is old, then you’d update the page. Otherwise, just load from cache.
For your convenience, here’s the simplified code Grimm posted to demonstrate.
1 2 3 4 5 6
When Ruby encounters the
throw, it zips back up the call stack looking for a matching symbol,
:halt. Where’s the
catch block though? It’s clearly not in the same method as the
throw. This means that it must be further up the stack. In other words,
#last_modified was called within a
1 2 3 4 5