Tips of exception handling in Ruby

In this post, I'd like to share with you some basic knowledge and tips about exception handling in Ruby.

Avoiding rescue with Exception

One common mistake when handling exceptions in Ruby is catching exceptions with an Exception instance, e.g rescue Exception => e.

The reason is very simple, let take a look at the Ruby Exception Hierarchy below:
Ruby Exception Hierarchy As you see, Exception is the root of all exception / error classes in Ruby, which also includes NoMemoryError, SyntaxError, Interrupt, etc.

By rescuing with Exception, you may inadvertently allow code with wrong syntax being deployed, or prevent user terminating your program in normal way, like the example below:

while true do  
  begin
    line = STDIN.gets
    # heavy processing
  rescue Exception => e
    puts "caught exception #{e.class}! ohnoes!"
  end
end  

When running this in console, users are unable to stop it by using CTRL + C.

Therefore, you should use StandardError instead when catching general exceptions, which also have a shorthand syntax rescue => e.

You can check this blog post for better understanding about this bad practice.

Custom Exception

We can define a custom exception class that inherit from the base class Exception, but usually we should inherit from StandardError or its subclasses:

class MyCustomError < StandardError; end  

We can also add additional attributes in our custom exception class to provide more context when handling the exception:

class MyCustomError < StandardError  
  attr_reader :object

  def initialize(object)
    @object = object
  end
end

# usage
begin  
  raise MyCustomError.new(user), 'Something broke'
rescue MyCustomError => e  
  e.object # => user
end  
raise / rescue synonyms

Following are some common uses of raise / rescue with their synonymous forms which can explain them a little bit clearer.

fail  
# is equivalent to
raise  
raise MyCustomError, 'Something broke'  
# is equivalent to
raise MyCustomError.new('Something broke')  
raise  
# is equivalent to
raise RuntimeError  
raise 'Something broke'  
# is equivalent to
raise RuntimeError, 'Something broke'  
begin  
rescue => e  
end  
# is equivalent to
begin  
rescue StandardError => e  
end  
raise / rescue vs throw / catch

The difference between raise / rescue and throw / catch has caused much confusion for ruby beginners, especially for those who came from Java or C++ background.

While raise / rescue is purely exception handling stuff for exceptional circumstances, throw / catch allow you to quick jump out of a block define by a specific symbol.

It come very handy if you want to break out of nested loops, for example:

found_person = nil  
catch(:found) do  
  people.each do |person|
    person.cars.each do |car|
      if car.name == 'Toyota Camry'
        found_person = person
        throw :found
      end
    end
  end
end  

In this example, we try to find the first person who own a Toyota Camry by looping through all cars of each person and stop right after we find the first car that matched.

throw also accept a return value beside the identifying symbol, so the previous example could be rewritten as:

found_person = catch(:found) do  
  people.each do |person|
    person.cars.each do |car|
      throw(:found, person) if car.name == 'Toyota Camry'
    end
  end
end  
Shorthand syntax to rescue a method

If you have some method like this:

def some_method  
  begin
    do_something!
  rescue
    # handle exception
  end
end  

You can make it a little bit cleaner by rewriting it as following:

def some_method  
  do_something!
rescue  
  # handle exception
end  
References