Ruby Exceptions -- Why "else"? Ruby Exceptions -- Why "else"? ruby ruby

Ruby Exceptions -- Why "else"?


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.


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"  endend

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_raiserescue ArgumentError => e  handle_the_errorend

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_errorrescue ArgumentError => e  handle_the_errorelse  do_something_else_when_the_previous_line_doesnt_raiseend


The else block in a begin rescue end block is used when you are perhaps expecting an exception of some sort to occur. If you run through all of your expected exceptions but still have nothing raised, then in your else block you can do whatever's needed now that you know that your original code ran error free.