Minimal re-raise
Sometimes we may want to rescue an exception, handle it depending on the context, and then re-raise it if we decide we can't handle it after all. In Ruby, we can do that with bare raise without any arguments, which will re-raise the currently rescued exception.
begin
# Code that might raise an exception...
rescue SomeError => e
if e.message.include?("specific condition")
# Handle the error...
else
raise
end
end
try {
// Code that might throw an error...
} catch (e) {
if (e instanceof SomeError && e.message.includes("specific condition")) {
// Handle the error...
} else {
throw e;
}
}
Yes, we kind of need to bring out the 🔎 to spot the difference here. I also wouldn't want to pretend that bare raise is very intuitive. Couldn't we just as well raise(e) to keep things explicit and obvious?
Well, sometimes we have to remember that Matz is also free to do whatever he feels like doing. And as long as the end result is me having to write less code to express the same thing, I'm not complaining.
Once you've seen it a few times, the raise without arguments idiom reads quite clearly as "re-raise the current exception". All's well that ends well!
Under the hood
Interestingly, raise isn't a keyword but is implemented as a method call, though it's implemented in C, which prevents us from inspecting its source code directly from IRB:
method(:raise) # => #<Method: Object(Kernel)#raise(*)>
method(:raise).source_location # => nil
Essentially, the way bare raise finds the right error object to re-raise is by looking up the current $! global variable, which is set by the previous raise call.
For those of us who aren't fluent in C, it can be helpful to refer to the TruffleRuby source code instead, where raise is mostly implemented in its own dialect of Ruby:
def raise(exc = undefined, msg = undefined, ctx = nil, cause: undefined, **kwargs)
# ...
if Primitive.undefined?(exc) && $!
exc = $!
else
# ...
end
end
If you want to dig deeper, you can check out kernel.rb at the TruffleRuby GitHub repo. Or if you're brave enough, you can dig through the C implementation in MRI Ruby's eval.c file, starting from rb_f_raise.