Why Is Throw and Catch Hardly Ever Used in Ruby

Where are catch and throw useful in Ruby?

Note: It looks like a few things have changed with catch/throw in 1.9. This answer applies to Ruby 1.9.

A big difference is that you can throw anything, not just things that are derived from StandardError, unlike raise. Something silly like this is legal, for example:

throw Customer.new

but it's not terribly meaningful. But you can't do:

irb(main):003:0> raise Customer.new
TypeError: exception class/object expected
from (irb):3:in `raise'
from (irb):3
from /usr/local/bin/irb:12:in `<main>'

What is the difference between Raising Exceptions vs Throwing Exceptions in Ruby?

I think http://hasno.info/ruby-gotchas-and-caveats has a decent explanation of the difference:

catch/throw are not the same as raise/rescue. catch/throw allows you to quickly exit blocks back to a point where a catch is defined for a specific symbol, raise rescue is the real exception handling stuff involving the Exception object.

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.

Why can't I catch the test exception in the caller method?

Jan beat me to it, but...

When you use the => var syntax with exception, you must specify what kind of exception you want to rescue. The base class for all exceptions is Exception, so it will work if you change that to rescue Exception => e. Also, when what you're rescuing from is the entire body of a method, you don't need an explicit begin...end block...

def test
raise Exception.new 'error'
end

def caller
test
rescue Exception =>e
puts e.message
end

caller()

Rescuing and catching at the same time

Its an opinion-based question, and one can argue either way. So, in my opinion...

If you are planning to return a value via throw, then, second option seems useful as it will let you rescue an error and throw some kind of default value.

Even if you were using throw and catch just to manage loop iterations and breaking out of it on certain conditions, second option still seems more readable and encapsulates all the logic inside the catch block.

Handling exceptions raised in a Ruby thread

If you want any unhandled exception in any thread to cause the interpreter to exit, you need to set Thread::abort_on_exception= to true. Unhandled exception cause the thread to stop running. If you don't set this variable to true, exception will only be raised when you call Thread#join or Thread#value for the thread. If set to true it will be raised when it occurs and will propagate to the main thread.

Thread.abort_on_exception=true # add this

def foo(n)
puts " for #{n}"
sleep n
raise "after #{n}"
end

begin
threads = []
[15, 5, 20, 3].each do |i|
threads << Thread.new do
foo(i)
end
end
threads.each(&:join)

rescue Exception => e

puts "EXCEPTION: #{e.inspect}"
puts "MESSAGE: #{e.message}"
end

Output:

 for 5
for 20
for 3
for 15
EXCEPTION: #<RuntimeError: after 3>
MESSAGE: after 3

Note: but if you want any particular thread instance to raise exception this way there are similar abort_on_exception= Thread instance method:

t = Thread.new {
# do something and raise exception
}
t.abort_on_exception = true

Raise custom exceptions, return constants, or return symbols? Why?

For something which indicates programmer error, such as the wrong type of argument passed to a method, definitely throw an exception. The exception will crash the program, drawing the programmer's attention to the fact that they are using your class incorrectly, so they can fix the problem. In this case, returning an error code wouldn't make sense, because the program will have to include code to check the return value, but after the program is debugged, such errors shouldn't ever happen.

In your WebCrawler class, is it expected that crawl will receive a bad URL as an argument sometimes? I think the answer is probably no. So raising an exception would be appropriate when a bad URL is passed.

When an exception is raised, the flow of execution suddenly "jumps" to the innermost handler. This can be a useful way to structure code when the exception is not expected to happen most of the time, because you can write the "main flow" of your method as simple, straight-line code without including a lot of details about what will happen when some rare error condition occurs. Those details can be separated from the "main flow" code, and put in an exception handler. When an error condition is expected to happen under normal conditions, though, it can be better to put the error handling code inline with the "main flow", to make it clearer what is going on. If the control flow of your program "jumps around" (as is the case when exceptions are used for normal flow control), that means the reader also has to "jump around" in the program text as they are figuring out how it works.

For the other two, I think it is expected that at least sometimes, the HTTP request will return an error code. To determine whether an exception or special return value is the best way to indicate such a condition, I would think about how often those conditions are going to happen under normal usage. Think also about how the client code will read either way. If you use exceptions, they will have to write something like:

urls.map do |url|
begin
crawl(url)
rescue PageNotFoundError
""
rescue UnauthorizedError
""
end
end

(By the way, I think this code example shows something: it might be a good idea if both of your custom exceptions inherit from a common superclass, so you can catch both of them with a single rescue clause if desired.) Or if you use error codes, it would look something like:

urls.map do |url|
response = crawl(url)
if [:page_not_found, :unauthorized].include? response
""
else
response
end
end

Which do you think reads better? It's really up to you. The one thing which you do not want to do is use integer constants for errors. Why use integers? When you print them in a debug trace, you'll have to go look at the list of constants to see what each one means. And using symbols is just as efficient computationally.



Related Topics



Leave a reply



Submit