这个救援案例有什么问题?

时间:2012-12-04 18:02:47

标签: ruby exception rescue

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

为什么不打印第二个“ok”?我无法弄清楚。我读过here ruby​​使用===运算符来匹配rescue子句的异常,但表面上并非如此。

我正在使用Ruby 1.9.3

编辑:所以在raise xx == yx === y不再成功之后似乎就是这样。这似乎是因为xy no longer have the same backtrace

4 个答案:

答案 0 :(得分:2)

我认为这是一个错误,或者说是Ruby 1.9的不足之处。请注意,Ruby 2.0引发了

TypeError: class or module required for rescue clause

在第8和14行。

请注意,raise不一定按照您的想法行事。当你raise一个对象时,实际上并没有提出那个对象,你提出了一个 new 对象,它是根据你传递的对象构造的。规则:

  • 如果对象响应exception,请在对象上调用exception并提高返回值
  • 如果对象是Exception的子类,请调用new并提高返回值
  • 否则失败
  • 如果上述任何方法的返回值不是Exception 的实例,则
  • 也会失败

所以,你 实际上正在筹集x,你正在筹集x.exception。尽管如此,根据Exception#exception x.exception的文档是x

答案 1 :(得分:1)

我只想在表格中添加一些东西:OP代码表明两个例外是相同的但它们不是 - 而且我想说明OP意味着什么:

  

所以看起来在做x之后,x == y和x === y不再成立。这似乎是因为x和y不再具有相同的回溯。

 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}

答案 2 :(得分:0)

编辑:为了澄清未来回复的问题,因为我认为我的回答不正确,这里的细微之处在于xy实例< / em>而不是 classes ,通常你会在raise语句中使用类。


如果预期的行为是在第二次救援时进行打印,y将无法解决问题。您正在引发类x的异常,并且您没有可以处理x的救援子句。如果你抓住StandardError(公共基类),你会看到为第二个块打印“ok”:

begin
  raise x
rescue StandardError
  puts "ok"
end

关于#===,我认为问题在于,当你加注时,你正在处理x而不是x实例作为一个类。

答案 3 :(得分:0)

似乎救援的定义是:

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

xy都不是error_type。它们是错误类型的实例。我不认为你真正按照你在那里的方式运行有效的代码。

如果你跑:

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

然后它将按预期工作。

另请注意,在Ruby 1.8上,x == yx === y都不会返回true