How to Use The “Begin” & “Rescue” Keywords in Ruby

A common problem in Ruby is that you get error messages, which in technical terms we call “exceptions”.

These exceptions can be expected, like a file that may be available sometimes but missing in others, or an API that is only available temporarily due to some restrictions, or they can be unexpected.

Today you’ll learn to manage expected errors.

How?

Well, let me introduce you to “begin” & “rescue” in Ruby, two important keywords used for handling error conditions.

How do they work?

First, you need to understand something.

Your Ruby programs can trigger an error at multiple points while they’re running.

Examples include:

  • Trying to read a non-existing file.
  • Dividing a number by zero.
  • A web server you’re working with has an outdated SSL certificate.

When an error happens… Ruby doesn’t crash right away!

You get a chance to recover from the error. We call this “exception handling”.

Ruby gives you a few keywords to implement error recovery in your code. These keywords are begin & rescue.

Let’s discover how to use them!

How to Handle Ruby Exceptions

How do you handle these exceptions?

You can wrap the code that raises the exception with a begin / rescue block.

Here’s how it works…

The first section (begin), has the code that you’re going to run & that may raise an exception.

Example:

begin
  IO.sysopen('/dev/null')
rescue
  # ...
end

Here we’re trying to open a file with sysopen. An exception is raised if we can’t open the file.

This is the perfect time to use the rescue keyword!

Using this keyword you can say what you want to happen when an exception is raised. So the failure mode is under your control.

Example:

begin
  IO.sysopen('/dev/null')
rescue
  puts "Can't open IO device."
end

You want to log this error & maybe provide some kind of default value.

Don’t. Ignore. Errors.

Rescuing Multiple Exceptions

You need to know that rescue takes an optional argument.

What is this argument?

This argument is the exception class that you want to rescue from.

It depends on what code you’re running.

For IO:

  • This may be Errno::ENOENT for a missing file
  • Or Errno::EACCES for a permission error

The best part?

You can handle multiple exceptions in the same begin/rescue block.

Like this:

begin
  IO.sysopen('/dev/null')
rescue Errno::ENOENT
  puts "File not found."
rescue Errno::EACCES
  puts "Insufficient permissions, not allowed to open file."
end

If you want to have the same action happen for multiple exceptions…

You can do this:

begin
  IO.sysopen('/dev/null')
rescue Errno::ENOENT, Errno::EACCES
  puts "There was an error opening the file."
end

Let’s keep learning!

How to Rescue Exceptions Inside Blocks & Methods

You don’t always have to use the begin keyword.

There are cases where you can leave it out.

Where?

Inside methods & blocks.

Example:

def get_null_device
  IO.sysopen('/dev/null')
rescue Errno::ENOENT
  puts "Can't open IO device."
end

The method definition itself does the work of begin, so you can omit it.

You can also do this with blocks.

Example:

["a.txt", "b.txt", "c.txt"].map do |f|
  IO.sysopen(f)
rescue Errno::ENOENT
  puts "Can't open IO device: #{f}."
end

Now, there is one more way to use the rescue keyword without begin.

Let’s see how that works.

Understanding Inline Rescue & Why Is It Dangerous

You can use rescue inline.

In a few rare scenarios, you may find this form of exception handling useful.

Here’s an example:

["a.txt", "b.txt", "c.txt"].select { |f| File.open(f) rescue nil }.map(&:size)

This allows you to open only the files that exist & ignore those that don’t.

As a result, you get the size for the existing files.

Without exceptions being raised.

Why do this?

Well, it lets you keep your code all in one line.

That’s about it.

There’s a “hidden danger” when using this form of rescue because you’re getting all exceptions descending from StandardError.

Which are most exceptions.

Why is that NOT good?

Because it’s best to only handle specific exceptions, instead of a broad selection of them.

This avoids hiding errors from yourself!

Hidden errors can lead to all kinds of strange behavior & hard-to-debug issues.

Summary

You’ve learned about errors in Ruby, basic exception handling & the rescue / begin keywords.

Please share this article if you found it helpful 🙂

Thanks for reading!

3 comments
Maimit Patel says a couple of years ago

Hi, I want to buy Ruby Deep Dive book but I am based in Japan 🇯🇵 is it possible to buy this book in English in japan

    Jesus Castello says a couple of years ago

    Hi.
    Yes, the book is in English, doesn’t matter where you buy it from 🙂

yura says a couple of years ago

Example with “.map(&:size)” will return sizes of existing file names strings, not files.

Comments are closed