Ruby's catch/throw, goto's little brother
Last week Pat Shaughnessy discovered that there’s a pre-processor definition in the Ruby 1.9 source code to enable goto and label statements in a rather ugly way:
The
__goto__
statement will cause the MRI Ruby 1.9 interpreter to jump up to the__label__
statement on the first line, since they both refer to the same symbol:loop
.
I don’t think there’s much use for goto
in a language like Ruby considering that it’s garbage collected and you can release resources using closures and ensure
blocks. However, if you’re dying to play with this, why not use goto’s little brother, catch
and throw
:
class Test
def foo
:label, "foo")
throw("should never get here"
end
def bar
"bar"
end
end
Test.new
test =
"foo -> " + catch(:label) {test.foo})
puts("bar -> " + catch(:label) {test.bar}) puts(
There’s not much to know about catch
and throw
. You give a symbol and a block to catch
, which acts like a goto label. Any code that uses throw
in that block, no matter how deep, will unwind and cause the catch
block to terminate, returning the value given to throw.
You can nest multiple catch
and throw
expressions using different values for the label symbol. Here’s another example:
def thrower
10.times do |i|
10.times do |j|
$stdout.puts("#{i}.#{j}")
:label, j) if i > 2 and j > 2
throw(end
end
end
:label) do
val = catch(
thrower"should never get here")
raise(end
$stdout.puts("val is #{val}")
Topics: Ruby