What is catch and throw used for in Ruby?
You can use this to break out of nested loops.
INFINITY = 1.0 / 0.0
catch (:done) do
1.upto(INFINITY) do |i|
1.upto(INFINITY) do |j|
if some_condition
throw :done
end
end
end
end
If you had used a break statement above, it would have broken out of the inner loop. But if you want to break out of the nested loop, then this catch/throw would be really helpful. I have used it here to solve one of the Euler problems.
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>'
ruby catch-throw and efficiency
In addition to being the "correct" way to get out of control structures, catch-throw
is also significantly faster(10 times as fast in my testing). Check out this gist for my code and results.
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 Throw-Catch tutorial unclear
That final line promptAndGet("Name")
does not get immediately executed, since it's after the catch
block.
The normal flow is that everything within the catch :quitRequested
block gets executed immediately, in order. That's why you get all 3 prompts inside. If you answer all 3 prompts, you'll also get to the prompt on the last line.
If you answer !
to any of the three prompts, the block will terminate. So you will not get the remaining prompts inside the block.
You'll still get the prompt on the last line since it's outside of the catch
.
throw
is what terminates the catch
block - not what initiates it.
Also, if you answer !
to that final prompt outside of the catch
block, you'll get an error, because the throw
was uncaught.
Common way of error-handling when using an external ruby-library
In ruby, you raise
an exception. The exception can be a class, an instance of a class, or a class and a string, and normally the class is a decendent of StandardError
, which is a decendent of Exception
. To catch an exception, you do this:
begin
raise StandardError, "weeee, exception!"
rescue StandardError => e # or => e for a general rescue
puts "Exception Message: #{e.message}"
end
Normally, the exception handling would be done outside the library so you'd only have to worry about the raise
line. throw
and catch
still exist in ruby, but raise
is the preferred method of exception handling.
What throwable can't be caught?
There are two types of error-handling flow in Ruby.
The most commonly used, and most flexible is begin...rescue
There is also throw
and catch
, which is much more lightweight. Some of the Rack libraries use this mechanism.
Most importantly, and sometimes annoyingly, the mechanisms are separate, and if a library uses throw...catch
for program flow, you cannot intercept the messages using begin...rescue
, therefore a rescue Exception
block will not get executed.
In general you should not try and intercept third-party throw
/catch
, unless documentation suggests that you can. That is because the library will probably have modified env
to set an error message/state. Instead, look for an equivalent method that does not work with Rack errors, but returns true
or false
for the conditions you are checking. For instance, perhaps the method user_signed_in?
would be better.
Example of how catch works in Ruby:
puts 'a'
x = catch(:mysymbol) do
puts 'b'
throw :mysymbol, 'world'
puts 'c'
end
puts "hello #{x}"
prints
a
b
hello world
Related Topics
How to Capitalize the First Letter in a String in Ruby
Rails 4: How to Reset Test Database
Sort Array Returned by Activerecord by Date (Or Any Other Column)
Ruby - Elegantly Convert Variable to an Array If Not an Array Already
Parse Command Line Arguments in a Ruby Script
How to Handle Constants in Ruby When Using Rails
What's the Difference Between Request.Remote_Ip and Request.Ip in Rails
How to List the Currently Available Objects in the Current Scope in Ruby
Read Input from Console in Ruby
Bcrypt Error: Devise Ruby 2.0 and Rails 4.0
What's the Best Way to Deploy a Jruby on Rails Application to Tomcat
Set Path for Original Images Using Paperclip in Rails
Check If Two Arrays Have the Same Contents (In Any Order)
How to Convert a Comma-Separated String into an Array
How to Password-Protect My /Sidekiq Route (I.E. Require Authentication for the Sidekiq::Web Tool)