Throw, Catch, Raise, Rescue… I’m so confused!

by Avdi Grimm on July 12, 2011

Throw, Catch, Raise, Rescue… I’m so confused!

This guest post is by Avdi Grimm, who is the author of “Exceptional Ruby“, an in-depth guide to exceptions and failure handling in Ruby. RubyLearning readers can get a $3 discount on the book by using code RUBYLEARN. Avdi has been hacking Ruby code for 10 years, and is still loving it. He is chief aeronaut at ShipRise, a consultancy specializing in sustainable software development and in helping geographically dispersed teams work more effectively. He lives in southern Pennsylvania with his wife and four children, and in his copious spare time blogs and podcasts at Virtuous Code and WideTeams.com.

Old keywords, new meanings

Avdi Grimm One of the aspects of Ruby that often confuses newbies coming from other languages is the fact that it has both throw and catch and raise and rescue statements. In this article I’ll try and clear up that confusion.

If you’re familiar with Java, C#, PHP, or C++, you are probably used to using try, catch, and throw for exception handling. You use try to delineate the block in which you expect an exception may occur. You use catch to specify what to do when an exception is raised. And you use throw to raise an exception yourself.

You’ve probably noticed that Ruby has throw and catch… but they don’t seem to be used the way you’re used to in other languages! And there are also these “begin“, “raise” and “rescue” statements that seem to do the same thing. What’s going on here?

Getting out fast

If you’ve done much programming in another language like Java, you may have noticed that exceptions are sometimes used for non-error situations. “exceptions for control flow” is a technique developers sometimes turn to when they want an “early escape” from a particular path of execution.

For instance, imagine some code that scrapes a series of web pages, looking for one that contains a particular text string.

def show_rank_for(target, query)
  rank = nil
  each_google_result_page(query, 6) do |page, page_index|
    each_google_result(page) do |result, result_index|
      if result.text.include?(target)
        rank = (page_index * 10) + result_index
      end
    end
  end
  puts "#{target} is ranked #{rank} for search '#{query}'"
end

show_rank_for("avdi.org", "nonesuch")

(For brevity, I’ve excluded the definitions of the #each_google_result_page and #each_google_result methods. You can view the full source at https://gist.github.com/1075364.)

Fetching pages and parsing them is time-consuming. What if the target text is found on page 2? This code will keep right on going until it hits the max number of result pages (here specified as 6).

It would be nice if we could end the search as soon as we find a matching result. We might think to use the break keyword, which “breaks out” of a loop’s execution. But break only breaks out of the immediately surrounding loop, and here we have a loop inside another loop.

This is a situation where we might come up with the idea of using an exception to break out of the two levels of looping. But exceptions are supposed to be for unexpected failures, and finding the results we were looking for is neither unexpected, nor a failure! What to do?

Throwing Ruby a fast ball

Ruby has given us a tool for just this situation. Unlike in other languages, Ruby’s throw and catch are not used for exceptions. Instead, they provide a way to terminate execution early when no further work is needed. Their behavior is very similar to that of exceptions, but they are intended for very different situations.

Let’s look at how we can use catch and throw to end the web search as soon as we find a result:

def show_rank_for(target, query)
  rank = catch(:rank) {
    each_google_result_page(query, 6) do |page, page_index|
      each_google_result(page) do |result, result_index|
        if result.text.include?(target)
          throw :rank, (page_index * 10) + result_index
        end
      end
    end
    "<not found>"
  }
  puts "#{target} is ranked #{rank} for search '#{query}'"
end

This time we’ve wrapped the whole search in a catch{...} block. We tell the catch block what symbol to catch, in this case :rank. When the result we are looking for is found, instead of setting a variable we throw the symbol :rank. We also give throw a second parameter, the search result :rank. This second parameter is the throw’s “payload”.

The throw “throws” execution up to the catch block, breaking out of all the intervening blocks and method calls. Because we gave the throw and catch the same symbol (:rank), the catch block is matched to the throw and the thrown symbol is “caught”.

The rank value that we gave as a payload to throw now becomes the return value of the catch block. We assign the result value to a variable, and proceed normally.

What if the search string is never found, and throw is never called? In that case, the loops will finish, and the return value of the catch block will be the value of the last statement in the block. We provide a default value (“<not found>”) for just this possibility.

catch and throw in the real world

The Rack and Sinatra projects provide a great example of how throw and catch can be used to terminate execution early. Sinatra’s #last_modified method looks at the HTTP headers supplied by the client and, if they indicate the client already has the most recent version of the page, immediately ends the action and returns a “Not modified” code. Any expensive processing that would have been incurred by executing the full action is avoided.

get '/foo' do
  last_modified some_timestamp
  # ...expensive GET logic...
end

Here’s a simplified version of the #last_modified implementation. Note that it throws the :halt symbol. Rack catches this symbol, and uses the supplied response to immediately reply to the HTTP client. This works no matter how many levels deep in method calls the throw was invoked.

def last_modified(time)
  response['Last-Modified'] = time
  if request.env['HTTP_IF_MODIFIED_SINCE'] > time
    throw :halt, response
  end
end

The way Rack uses catch/throw illustrates an important point: the throw call does not have to be in the same method as the catch block.

Conclusion

Ruby is a language that tries to anticipate your needs as a programmer. One common need is a way to terminate execution early when we find there is no further work to be done. Unlike in some languages, where we would have to either abuse the exception mechanism or use multiple loop breaks and method returns to achieve the same effect, Ruby provides us with the catch and throw mechanism to quickly and cleanly make an early escape. This leaves begin/raise/rescue free to be used for errors, and nothing else.

I hope you found this article valuable. Feel free to ask questions and give feedback in the comments section of this post. Thanks!

Technorati Tags: , ,

Posted by Avdi Grimm

{ 29 comments… read them below or add one }

Liam Morley July 12, 2011 at 6:59 pm

As a Java programmer years ago, “exceptions as control flow” was a technique that poor developers used because they didn’t know better, and smarter developers would despise. I carried this practice into my Ruby development, but now it seems I was only half right… I’m right not to use raise, but I can use throw. This is good to know! http://c2.com/cgi/wiki?DontUseExceptionsForFlowControl on the C2 wiki also explains what’s wrong with exceptions for flow control, but distinguishes the use of throw/catch as ok in Ruby.

Reply

Avdi Grimm July 12, 2011 at 8:38 pm

I’m glad I was able to help clarify that for you! And thank you for linking to that WikiWiki page, I probably should have linked to that from the article.

Reply

Joe July 12, 2011 at 9:19 pm

The method definition would need to be restructured a bit (drop the puts at the end), but if that were possible, can you explain why using ‘return’ would be wrong?
>> return (page_index * 10) + result_index

Reply

Avdi Grimm July 13, 2011 at 3:08 am

It would not be wrong to restructure the code that way.

What’s unique about “throw” is that, as in the Sinatra/Rack example, it can break through multiple levels of method calls.

Imagine that we found we had to add a bunch more logic to the inside of that double loop. And then we decided to extract that logic out into a separate method. The cool thing about “throw” is that we could extract out the code *exactly* as is into a new method, and it would still work, because the *throw* would still propagate all the way up to the containing “catch”.

Does that make sense?

Reply

John Chow July 14, 2011 at 4:09 am

Thanks for the great post, Avdi! I recently switched from C# (ASP.NET) to Ruby (on Rails), and I’m still learning the different paradigm Ruby encompasses. Ruby’s try/catch usage is very elegant, and I’m starting to see why people love the expressive language so much.

Keep up the great work, I hope to learn more from you in the future!

Reply

Avdi Grimm July 15, 2011 at 12:42 am

Welcome to Ruby, John!

Reply

Pedro Nascimento July 14, 2011 at 10:02 pm

I’ve always felt try and catch blocks in Java were evil, and never tried to use them in Ruby except where I really needed. Didn’t know about throw/catch. I’d use return as Joe suggested, but your reply also gives you a nice example of why would you not always use it. Nice catch! :)

Reply

Avdi Grimm July 15, 2011 at 12:50 am

Yeah, I don’t use throw/catch often, but when I do it’s a great help.

Reply

Will July 14, 2011 at 10:38 pm

When I need to break out of multiple loops, I usually just put the nested loops into a method and use ‘return’ to break out of all of them.

But your point in a comment above is absolutely that throw/catch is in a sense more robust — you can refactor with more impunity. That said, this situation has never occurred for me. I’ve been programming ruby for many years, and honestly the only time I’ve ever used throw/catch is specifically to “throw :halt” in Sinatra. I’ve never encountered it elsewhere or thought it would be useful to improve my code anywhere.

Reply

Avdi Grimm July 15, 2011 at 12:51 am

I use throw/catch from time to time, but it’s almost always in the context of library or framework code–rarely in application code.

Reply

Edward Price July 15, 2011 at 12:13 pm

Wow, I have been doing this stuff for about 4.5 years and thought this was — once again — party of Ruby’s tmtowtdi nature. Where were you in my younger years when I was using explicit returns by the bushel? :)

Thanks.

Reply

Avdi Grimm July 15, 2011 at 10:31 pm

There’s nothing wrong with an explicit return! But throw is definitely one more “way to do it” to keep in mind for some situations.

Reply

Tom Corbin July 16, 2011 at 3:47 am

I was wondering why you use curly braces in the catch instead of do/end?

I usually see the curlies used on one line.

Reply

Avdi Grimm July 16, 2011 at 9:11 am

I use the functional/procedural convention for blocks: curlies for functional blocks, where the block is used for its result value. do…end for procedural blocks, where the block is being evaluated for its side effects.

Reply

Paul Dirac July 16, 2011 at 8:49 am

Can’t believe it’s only now that I learn something this useful. Thanks for the informative article.

Reply

Avdi Grimm July 16, 2011 at 9:12 am

Glad it was helpful!

Reply

Martin Streicher July 18, 2011 at 7:02 am

Avdi, thanks for the post. Well written and informative.

Reply

craig July 18, 2011 at 6:43 pm

Very cool, I’m also relatively new to ruby and I hadn’t seen this before.

Reply

Ben July 21, 2011 at 9:36 am

Nice explanation, thanks.

Quick fix: if request.env['HTTP_IF_MODIFIED_SINCE'] >= time

Reply

Bruno Eustáquio November 1, 2011 at 7:50 am

Thanks Avdi,
I could see that the try / catch can help us clean up the code (ruby philosophy that takes seriously). And as you yourself pointed out in the comments do not need to use it all the time, but when appropriate, the code becomes more beautiful.

Reply

Ben December 22, 2011 at 2:30 am

In other words, throw/catch are just goto and :label that you can attach a payload to. But hey, it’s ruby, let’s be different! It’s fun!

Reply

Avdi Grimm December 30, 2011 at 11:52 am

There is a fundamental difference between throw/catch and goto. Goto, in languages which support it, pays no attention to the stack. Any resources which were allocated before the goto are simply left dangling unless they are manually cleaned up.

throw/catch, like exception handling, unwinds the stack, triggering ensure blocks along the way. So, for example, if you throw inside an open() {…} block, the open file will be closed on the way up to the catch() block.

Reply

Sergez January 30, 2012 at 3:24 pm

Avdi, thanks a lot for great explanation! Excellent!

Reply

Chris May 7, 2012 at 5:22 pm

Back in the day when I was programming Java, I would have used its ‘named loop’ approach for explicitly stating which loop you’d like to break.

See http://stackoverflow.com/questions/886955/breaking-out-of-nested-loops-in-java

Reply

Willie October 10, 2012 at 9:24 pm

Great explanation, thanks very helpful.
I come from Java so I’m used to the compiler warning me if one of the methods I am calling is going to throw something. What would you consider best practices for putting a throw in a method? What would happen if you weren’t aware you needed to catch anything (e.g. if the method was in a third party library)?

Reply

Johannes November 8, 2012 at 12:58 pm

Thank you for this well-written article!
I had never distinguished between both concepts.

Reply

Marc Beaupre May 28, 2013 at 8:37 pm

FWIW, I think it’s cleaner to use the Enumberable’s `find` method http://www.ruby-doc.org/core-1.9.3/Enumerable.html#method-i-find

I think it’s expresses intent better than using catch/throw. But, this might be personal opinion.

Reply

Dan August 29, 2013 at 6:43 pm

Great stuff, Avdi. Having done Ruby for a while now, I am deliberately try to learn all the nooks and crannies I’ve missed to be more well rounded, and this is one. Great explanation.

Hey, saw you give a talk at RubyCon a couple years back and it was great. You’re an excellent teacher and your efforts to educate others are appreciated!

Reply

Avdi Grimm September 2, 2013 at 4:14 am

Thanks so much!

Reply

Leave a Comment

{ 112 trackbacks }

Previous post:

Next post: