Question

x = StandardError.new(:hello)
y = StandardError.new(:hello)
x == y # => true
x === y # => true

begin
  raise x
rescue x
  puts "ok" # gets printed
end

begin
  raise x
rescue y
  puts "ok" # doesn't get printed
end

Why isn't the second "ok" printed? I can't figure it out. I've read here that ruby uses the === operator to match exceptions to rescue clauses, but that's ostensibly not the case.

I'm using Ruby 1.9.3

EDIT: So it seems like that after doing raise x, x == y and x === y no longer hold. It seems to because x and y no longer have the same backtrace.

Was it helpful?

Solution 2

I just want to add something to the table: OP code suggests that the two exceptions are the same but they are not - furthermore i want to illustrate what OP meant with:

So it seems like that after doing raise x, x == y and x === y no longer hold. It seems to because x and y no longer have the same backtrace.

 x = StandardError.new(:hello)
 y = StandardError.new(:hello)
 class Object
   def all_equals(o)
     ops = [:==, :===, :eql?, :equal?]
     Hash[ops.map(&:to_s).zip(ops.map {|s| send(s, o) })]
   end
 end

 puts x.all_equals y # => {"=="=>true, "==="=>true, "eql?"=>false, "equal?"=>false}

 begin
   raise x
 rescue
   puts "ok" # gets printed
 end

 puts x.all_equals y # => {"=="=>false, "==="=>false, "eql?"=>false, "equal?"=>false}

OTHER TIPS

I think that's a bug, or rather an underspecification of Ruby 1.9. Note that Ruby 2.0 raises a

TypeError: class or module required for rescue clause

on lines 8 and 14.

Note that the raise doesn't necessarily do what you think it does, either. When you raise an object, you don't actually raise that object, you raise a new object which is constructed from the object you passed according to these simple rules:

  • if the object responds to exception, call exception on the object and raise the return value
  • if the object is a subclass of Exception, call new and raise the return value
  • otherwise fail
  • also fail if the return value of any of the above methods is not an instance of Exception

So, you are not actually raising x, you are raising x.exception. According to the documentation of Exception#exception x.exception is x, though.

It seems that the definition for rescue is:

[rescue [error_type [=> var],..]

Neither x nor y is stricly an error_type. They are instances of an error type. I don't think you're really running valid code the way you're doing it there.

If you run:

begin
  raise x
rescue y.class
  puts "ok"
end

Then it will work as expected.

Also note that on Ruby 1.8 neither x == y nor x === y returns true.

EDIT: To clarify the issue for future responses, because I think my response is incorrect, the subtlety here is that x and y are instances rather than classes, and normally you would use classes in a raise statement.


If the intended behavior is to print at the second rescue, the y will not do the trick. You are raising an exception of class x, and you don't have a rescue clause that will handle x. You would see "ok" printed for the second block if you caught StandardError, the common base class, though:

begin
  raise x
rescue StandardError
  puts "ok"
end

Regarding #===, I think the issue is that when you raise, you're dealing with an instance of x rather than x as a class.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top