I've known but never used Ruby's catch and throw until tackling an Advent of Code problem. This particular challenge had me building multiple nested iterators, in short scanning a 3d array – a set of game boards which contain rows which contain cells.
boards.each do |board|
board.each_with_index do |row, row_idx|
row.each_with_index do |col, col_idx|
# if <condition> exit all the enumerators
end
end
end
So the question was, how to simply break out of all the iterators if a condition was met on line 4. One simple way to do this would be to factor this block out to a method and have a return statement here but there is another way.
While ruby does have first-class support for exception handling, catch and throw are not used for exceptions but for control-flow. They are made for exactly the problem I'm trying so solve; breaking out of a stack several levels deep and returning control to the a point much earlier.
This is what they look like:
catch(:something) do
loop do
throw :something
end
end
The throw statement immediately returns to calling block. In fact we can use this to return values to the outer block
value = catch(:something) do
loop do
throw :something, 'foobar'
end
end
Here, value will become equal to foobar at the point line 3 executes.
This helps me write my block like this:
score = catch(:won) do
boards.each do |board|
board.each_with_index do |row, row_idx|
row.each_with_index do |col, col_idx|
# do some work
throw :won, some_value if condition
end
end
end
end
The full block of code is on GitHub.