tags:

views:

162

answers:

4

I really don't see a sane use for these. There is already rescue and raise, so why the need for throw and catch? It seems they are supposed to be used to jump out of deep nesting, but that just smells like a goto to me. Are there any examples of good, clean use for these?

+2  A: 

It's basically a goto, and slightly more akin to a call/cc, except that the control flow is wired up implicitly by name instead of explicitly as a parameter. The difference between throw/catch and raise/rescue is that the former is intended to be used for control flow instead of only exceptional situations, and it doesn't waste time putting together a stack trace.

Sinatra uses throw/catch for HTTP error codes, where a handler can use throw to cede control to the Sinatra library in a structured way. Other sorts of HTTP frameworks use exceptions, or by returning a different class of response, but this lets Sinatra (for example) try another request handler after catching it.

jleedev
+5  A: 

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>'
John Feminella
The example is legal with a rescue/raise, too. It just says "class or module required for rescue clause".
jleedev
Also, it's telling me that you can only catch/throw a symbol.
jleedev
@jleedev » Which version of Ruby are you running? [edit: Oops, I misread what you said. I badly typoed that last sentence, will fix.]
John Feminella
Your example with exceptions instead of symbols: http://codepad.org/nBBok3hg
jleedev
In ruby 1.8.6. Throwing/raising things of the wrong type: http://codepad.org/oFYEardW
jleedev
@jleedev: In 1.9 this works, so that explains it. Interesting!
John Feminella
I think throw must also see an active 'catch' block in order to work, much like raise must see an active 'rescue'. If there's no matching catch block, there would be NameError.
bryantsai
+1  A: 

The difference between the two is that you can only 'raise' exceptions but can 'throw' anything (1.9). Other than that, they should be interchangeable, that is, it should be possible to rewrite one with another, just like the example given by @john-feminella.

bryantsai
+3  A: 

They can be really useful in simplifying DSLs for end users by passing control out of the DSL without the need for complex case / if statements

I have a Ruby app which allows users to extend it via an internal DSL. Some of the functions in the DSL need to return control to specific parts of my application. Let's take a simple example. Suppose the user is developing a simple extension concerning dates

if today is a holiday then
   do nothing
end

week_of_year = today.week.number

if week_of_year < 10 then

...

The do nothing bit triggers a throw which passes control out of the exec statement and back to me.

Rather than continuing to execute the DSL, on some condition, we want it to exit and hand control back to my application. Now you could get the user to use lots of embedded if statements and have the DSL end naturally but that just obscures what the logic is trying to say.

Throw really is a goto which is 'considered dangerous' but damn it sometimes they are the best solution.

Chris McCauley