Ruby Exceptions -- Why "Else"

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.

Ruby exception handling in else block

In the begin rescue block else is called only when no exception occurs, i.e. no error was thrown. Try this:

begin
# do work here
rescue SafeShutdown => e
# print e
rescue SystemExit => e
# print e
else
# this will only run when no exceptions are thrown
ensure
# this will always run
end

Ruby exception handling

You're thinking about exceptions incorrectly.

The whole point of exceptions is that the main body of your code proceeds as though no exceptions were thrown. All of the code inside your begin block is already executing as though no exceptions were thrown. An exception can interrupt the normal flow of code, and prevent subsequent steps from executing.

You should put your file copy stuff inside the begin block:

begin
#code...
# This will run if the above "code..." throws no exceptions
FileUtils.cp("Demo.rb", "C:/Ruby/Success")
rescue StandardError => e
puts("Exception #{e} occurred")
puts("Copying script to error folder.")
FileUtils.cp("Demo.rb", "C:/Ruby/Failure")
end

Ruby Exception or Error?

Looking at the list of Ruby exceptions, SignalException is the only one that is named *Exception; everything else is an XXXError (except for SystemExit and fatal). If anything, the practice is to name your exception FooError. I'm having trouble finding any specific reason why SignalException isn't named SignalError.

Ruby one-liner and warning: 'else without rescue is useless'

Ruby is interpreting your code as two separate statements.

puts "#{n} must be odd and positive" if n % 2 == 0 || n < 0

else (n + 1).times { |i| puts chars * i }

The else is not associated with the if. Ruby is, I guess in desperation, interpreting that as part of a begin/rescue/else condition. Why it's not a syntax error, I have no idea, but it's interpreting it as having no begin block which is technically "successful" so the else always runs.

Conditional statements like do this if that are intended only for simple conditions covering simple statements. Trying to wedge in an else condition is right out. Instead, use a normal condition.

def triangle(n, chars)
if n % 2 == 0 || n < 0
puts "#{n} must be odd and positive"
else
(n + 1).times { |i| puts chars * i }
end
end

In general, avoid the else entirely by taking care of the error condition immediately. It avoids nesting the bulk of the function in an else block. This is more relevant for longer functions, but it's a good habit to get into.

def triangle(n, chars)
if n % 2 == 0 || n < 0
puts "#{n} must be odd and positive"
return nil
end

(n + 1).times { |i| puts chars * i }
end

Finally, errors should be handled with exceptions, not printed error messages. Exceptions can be caught and dealt with by the caller, printed error messages are difficult to catch and bubble up to the user. Exceptions, if not dealt with, halt the program; if the user forgets to handle an error the program will stop and they'll know to fix it. Printed error messages simply let the program plow forward and can be ignored leading to further problems down the road.

Exceptions can also be categorized allowing the caller to figure out what sort of error happened and act appropriately.

def triangle(n, chars)
if n % 2 == 0 || n < 0
raise ArgumentError, "#{n} must be odd and positive"
end

(n + 1).times { |i| puts chars * i }
end

Why is it bad style to `rescue Exception = e` in Ruby?

TL;DR: Use StandardError instead for general exception catching. When the original exception is re-raised (e.g. when rescuing to log the exception only), rescuing Exception is probably okay.


Exception is the root of Ruby's exception hierarchy, so when you rescue Exception you rescue from everything, including subclasses such as SyntaxError, LoadError, and Interrupt.

Rescuing Interrupt prevents the user from using CTRLC to exit the program.

Rescuing SignalException prevents the program from responding correctly to signals. It will be unkillable except by kill -9.

Rescuing SyntaxError means that evals that fail will do so silently.

All of these can be shown by running this program, and trying to CTRLC or kill it:

loop do
begin
sleep 1
eval "djsakru3924r9eiuorwju3498 += 5u84fior8u8t4ruyf8ihiure"
rescue Exception
puts "I refuse to fail or be stopped!"
end
end

Rescuing from Exception isn't even the default. Doing

begin
# iceberg!
rescue
# lifeboats
end

does not rescue from Exception, it rescues from StandardError. You should generally specify something more specific than the default StandardError, but rescuing from Exception broadens the scope rather than narrowing it, and can have catastrophic results and make bug-hunting extremely difficult.


If you have a situation where you do want to rescue from StandardError and you need a variable with the exception, you can use this form:

begin
# iceberg!
rescue => e
# lifeboats
end

which is equivalent to:

begin
# iceberg!
rescue StandardError => e
# lifeboats
end

One of the few common cases where it’s sane to rescue from Exception is for logging/reporting purposes, in which case you should immediately re-raise the exception:

begin
# iceberg?
rescue Exception => e
# do some logging
raise # not enough lifeboats ;)
end

Ruby - how to check for specific exception after rescue?

In your example ex is an instance of on exception, and will not give expected results when comparing to the class itself with the == operator, which is a Class object. You need to check the error's class against the actual class object.

# A NameError or an error that is a subclass of NameError
ex.is_a?(NameError)
ex.kind_of?(NameError)

# NameError ONLY, not even sub-classes of NameError
ex.instance_of?(NameError)

There are numerous other ways you could check, though the previous methods I outlined are the most commonly used, and typically "preferred" way. Although equality could still be used on the classes, it is typically not the suggested way to achieve it, as it is less flexible, and less clear on the underlying intention, where the above examples indicate exactly how you would like to handle subclasses of the specified error.

ex.class == NameError

I found this answer that explains the subtle differences pretty well.



Related Topics



Leave a reply



Submit