Tuesday, March 22, 2011

Ruby rescue gotcha

Today in the codebase at my day job, I found a particularly cute bug related to "rescue" in Ruby. This isn't a particularly unknown gotcha–I've read about it on the net at least once–but this is a particularly sweet (or devious) manifestation, and as far as I can tell it was purely accidental, not a contrived example.

The following example uses rspec, but the key thing is begin; 2 / 0 rescue NoMethodError; end versus begin; 2 / 0; rescue NoMethodError; end–what is the difference between these two statements?


require File.expand_path('spec_helper', File.dirname(__FILE__))

describe "enigma" do
it "fails, but why" do
lambda { begin; 2 / 0 rescue NoMethodError; end }.
should raise_error(ZeroDivisionError)
end

it "passes, but why" do
lambda { begin; 2 / 0; rescue NoMethodError; end }.
should raise_error(ZeroDivisionError)
end
end


I'll post the answer in a comment in a few days; feel free to post your answers as comments if you wish.

2 comments:

george.anderson said...

The inline rescue is swallowing the ZeroDivisionError exception (as expected with inline rescues) and returning the NoMethodError class. See: https://gist.github.com/884026

Jim Kingdon said...

George Anderson pretty much said it all. To summarize: "2 / 0 rescue NoMethodError" rescues all exceptions and returns NoMethodError (a class). The one including "2 / 0; rescue NoMethodError" rescues just NoMethodError (which is probably what the person writing this code meant). The cute thing about this particular example is that it is just a single semicolon which is different between the two.