Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby Exceptions -- Why "else"?

I'm trying to understand exceptions in Ruby but I'm a little confused. The tutorial I'm using says that if an exception occurs that does not match any of the exceptions identified by the rescue statements, you can use an "else" to catch it:

begin   # -   rescue OneTypeOfException   # -   rescue AnotherTypeOfException   # -   else   # Other exceptions ensure # Always will be executed end 

However, I also saw later in the tutorial "rescue" being used without an exception specified:

begin     file = open("/unexistant_file")     if file          puts "File opened successfully"     end rescue     file = STDIN end print file, "==", STDIN, "\n" 

If you can do this, then do I ever need to use else? Or can I just use a generic rescue at the end like this?

begin   # -   rescue OneTypeOfException   # -   rescue AnotherTypeOfException   # -   rescue # Other exceptions ensure # Always will be executed end 
like image 793
Kvass Avatar asked Jun 08 '11 14:06

Kvass


People also ask

How do you handle exceptions in Ruby?

Ruby also provides a separate class for an exception that is known as an Exception class which contains different types of methods. The code in which an exception is raised, is enclosed between the begin/end block, so you can use a rescue clause to handle this type of exception.

Does raising an error stop execution Ruby?

When you raise an exception in Ruby, the world stops and your program starts to shut down. If nothing stops the process, your program will eventually exit with an error message.

Can we use rescue without begin?

The method definition itself does the work of begin , so you can omit it. You can also do this with blocks. Now, there is one more way to use the rescue keyword without begin .

Does Ruby have try catch?

In Ruby we have a way to deal with these cases, we have begin, end(default try catch) and we can use try and catch, both try catch and raise rescue used for the same purpose, one will throw exception(throw or raise) with any specific name inside another(catch or rescue).


2 Answers

The else is for when the block completes without an exception thrown. The ensure is run whether the block completes successfully or not. Example:

begin   puts "Hello, world!" rescue   puts "rescue" else   puts "else" ensure   puts "ensure" end 

This will print Hello, world!, then else, then ensure.

like image 64
Chris Jester-Young Avatar answered Sep 22 '22 23:09

Chris Jester-Young


Here's a concrete use-case for else in a begin expression. Suppose you're writing automated tests, and you want to write a method that returns the error raised by a block. But you also want the test to fail if the block doesn't raise an error. You can do this:

def get_error_from(&block)   begin     block.call   rescue => err     err  # we want to return this   else     raise "No error was raised"   end end 

Note that you can't move the raise inside the begin block, because it'll get rescued. Of course, there are other ways without using else, like checking whether err is nil after the end, but that's not as succinct.

Personally, I rarely use else in this way because I think it's rarely needed, but it does come in handy in those rare cases.

EDIT

Another use case occurred to me. Here's a typical begin/rescue:

begin   do_something_that_may_raise_argument_error   do_something_else_when_the_previous_line_doesnt_raise rescue ArgumentError => e   handle_the_error end 

Why is this less than ideal? Because the intent is to rescue when do_something_that_may_raise_argument_error raises ArgumentError, not when do_something_else_when_the_previous_line_doesnt_raise raises.

It's usually better to use begin/rescue to wrap the minimum code you want to protect from a raise, because otherwise:

  • you may mask bugs in the code that wasn't supposed to raise
  • the intention of rescue is harder to decipher. Someone (including your future self) may read the code and wonder "Which expression did I want to protect? It looks like expression ABC... but maybe expression DEF too???? What was the author intending?!" Refactoring becomes much more difficult.

You avoid those problems with this simple change:

begin   do_something_that_may_raise_argument_error rescue ArgumentError => e   handle_the_error else   do_something_else_when_the_previous_line_doesnt_raise end 
like image 42
Kelvin Avatar answered Sep 19 '22 23:09

Kelvin