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 iden
The only reason I can see for the else
block is if you want to execute something before the ensure
block when the code in the begin
block didn't raise any errors.
begin
puts "Hello"
rescue
puts "Error"
else
puts "Success"
ensure
puts "my old friend"
puts "I've come to talk with you again."
end
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.
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 rescue
d. 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:
raise
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
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
.
Thanks to else
you sometimes can merge two nested begin end
blocks.
So (simplified example from my current code) instead of:
begin
html = begin
NetHTTPUtils.request_data url
rescue NetHTTPUtils::Error => e
raise unless 503 == e.code
sleep 60
retry
end
redo unless html["market"]
end
you write:
begin
html = NetHTTPUtils.request_data url
rescue NetHTTPUtils::Error => e
raise unless 503 == e.code
sleep 60
retry
else
redo unless html["market"]
end