Ruby's catch/throw, goto's little brother

Posted by Peter Jones on

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
    throw(:label, "foo")
    "should never get here"
  end

  def bar
    "bar"
  end
end

test = Test.new

puts("foo -> " + catch(:label) {test.foo})
puts("bar -> " + catch(:label) {test.bar})

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}")
      throw(:label, j) if i > 2 and j > 2
    end
  end
end

val = catch(:label) do
  thrower
  raise("should never get here")
end

$stdout.puts("val is #{val}")