• 20 hours
  • Medium

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 10/4/17

Exceptions and rescue

Log in or subscribe for free to enjoy all this course has to offer!

In every system, no matter how well designed, there will come a time when things don't go as planned. Sometimes, an argument is passed that isn't what we thought it would be. Sometimes someone tries to divide by 0. Sometimes you just type something wrong. When these situations arise, programs usually fail and exit. Ruby is no different, but along the way, it tries to manage the failure by creating an object called an Exception.

Exceptions

Exceptions have different classes that attempt to describe the circumstances that gave rise to the system's failure. The really useful thing about exceptions, though, is that they allow us to anticipate failure, and, in some cases, recover from them to save our program. This is even used as a way of bubbling messages up through the call stack.

TheExceptionclass handles nearly every kind of error that can happen while your program is running, including syntax errors.

To see an example, let's create a new file called error.rb:

error.rb

asldkfj

Here's an example, caused by typing some random letters and trying to run them as Ruby code:

$ ruby error.rb
error.rb:1:in `<main>': undefined local variable or method `asldkfj' for main:Object (NameError)

Like so much else in the language, errors are objects in Ruby. In this case, we have aNameError, as the output helpfully tells you in the parentheses at the end there. If we look at theNameErrorclass' ancestry, we see that it inherits fromException:

irb(main):0> NameError.ancestors
=> [DidYouMean::Correctable, NameError, StandardError, Exception, Object, Kernel, BasicObject]

Rescuing Errors

You might think "That's great that errors are objects, but what's the point of that? Your program crashes as soon as they're created, so how could you ever use them?"

In fact you couldn't make use of error objects for that very reason, except for a special method in Ruby calledrescue.

rescuestops your program from crashing temporarily after an exception is created, and gives you a chance to recover, should you so choose. To userescue, you need to set it up with a special context block using the wordsbeginandend. Here's how that looks:

error.rb

begin
  asldkj
rescue
  puts "um, that makes no sense"
end

Now running that file produces:

$rubyerror.rb
um,thatmakesnosense

Rescuing exceptions works almost like an if/else statement. You can even useelseif you want to

error.rb

begin
  "no errors here"
rescue
  puts "um, that makes no sense"
else
  puts "everything's fine, no errors"
end
$rubyerror.rb
everything'sfine,noerrors

Unfortunately, usingrescuelike this will catch any kind of error that occurs while the begin...end block is running, even one's that we weren't expecting.

error.rb

begin
  1/0
rescue
  puts "um, that makes no sense"
end
$rubyerror.rb
um,thatmakesnosense

Actually this isn't good, because1/0"makes sense", in a way, it's just something that isn't allowed. To narrow down the conditions that we want to rescue, pass the name of the exception class you want to rescue as an argument:

error.rb

begin
  asldkf
rescue NameError
  puts "That thing you wrote doesn't exist, sorry."
end
$rubyerror.rb
Thatthingyouwrotedoesn'texist,sorry.

But now if a different type of error happens inside the begin...end block...

error.rb

begin
  1/0
rescue NameError
  puts "That thing you wrote doesn't exist, sorry."
end

 

$ ruby error.rb 
error.rb:2:in `/': divided by 0 (ZeroDivisionError)
from error.rb:2:in `<main>'

Note thatrescuewon't address any errors that happen outside your begin...end block:

error.rb

begin
  "nothing special here"
rescue
  puts "um, that makes no sense"
end

askjdf
$ ruby error.rb 
error.rb:7:in `<main>': undefined local variable or method `askjdf' for main:Object (NameError)

 

Capturing exceptions

So far we've talked about controlling for which class of exception we want torescue. This allows us to respond in specific ways to different types of errors. But sometimes we want more information than just the type of error. We might want to know the exact error message, or the line on which it happened.

To enable this, rescue gives us the option to store the actual exception instance in a variable, so that we can call methods on it later to get more information.

error.rb

begin
  asldkfj
rescue NameError => my_error_object
  puts my_error_object.message
  puts my_error_object.backtrace
end
$ ruby error.rb 
undefined local variable or method `asldkfj' for main:Object
error.rb:2:in `<main>'

The above code printed out the error's message as well as its stack trace.

 

raise

The counterpart to rescue is raise. It allows you to create an error out of thin air!

In the examples above, see what happens if you replace the lines with a random error-generating string on them: 

  asldkfj

with this statement:

  raise NameError

See if you can figure out how to do the same thing with this error:

  1/0

Finally, see what happens when you do the following:

raise

with no argument afterwards.

Try it in a file all by itself, then try it inside one of your rescue blocks. 

Example of certificate of achievement
Example of certificate of achievement