throw and catch are Kernel methods that define a control structure
that can be thought of as a multilevel break
.
throw
doesn’t just
break
out of the current loop or block but can actually transfer
out any number of levels, causing the block defined with a catch
to exit.
The catch
need not even be in the same method as the throw
.
It can be in the calling method, or somewhere even further up the
call stack.
Languages like Java and JavaScript allow loops to be named or labeled with an arbitrary prefix. When this is done, a control structure known as a labeled break causes the named loop to exit.
Ruby’s
catch
method defines a labeled block of code, and Ruby’s throw
method causes that block to exit.
But throw
and catch
are much more
general than a labeled break
. For one, it can be used with any kind
of statement and is not restricted to loops.
More profoundly, a
throw
can propagate up the call stack to cause a block in an invoking
method to exit.
If you are familiar with languages like Java and JavaScript, then
you probably recognize throw
and catch
as the keywords those languages
use for raising and handling exceptions.
Ruby does exceptions
differently, using raise
and rescue
, which we’ll learn about later
in this chapter.
But the parallel to exceptions is intentional.
Calling throw
is very much like raising an exception.
And the way
a throw
propagates out through the lexical scope and then up the
call stack is very much the same as the way an exception propagates out and up.
Despite the similarity to exceptions, it is
best to consider throw
and catch
as a general-purpose (if perhaps
infrequently used) control structure rather than an exception
mechanism.
If you want to signal an error or exceptional condition,
use raise
instead of throw
.
The following code demonstrates how throw
and catch
can be used to break
out of nested loops:
for matrix in data do # Process a deeply nested data structure. catch :missing_data do # Label this statement so we can break out. for row in matrix do for value in row do throw :missing_data unless value # Break out of two loops at once. # Otherwise, do some actual data processing here. end end end # We end up here after the nested loops finish processing each matrix. # We also get here if :missing_data is thrown. endNote that the
catch
method takes a symbol argument and a block.
It executes the block and returns when the block exits or when the specified symbol is thrown.
throw
also expects a symbol as its
argument and causes the corresponding catch
invocation to return.
If no catch
call matches the symbol passed to throw
, then a
NameError
exception is raise
d.
Casiano Rodriguez León 2015-01-07