5
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”?我想不通。我在这里读到ruby​​ 使用===运算符将​​异常与救援子句匹配,但表面上并非如此。

我正在使用 Ruby 1.9.3

编辑:所以似乎在做之后,raise x不再持有。似乎因为和不再有相同的回溯x == yx === yxy

4

4 回答 4

3

我认为这是一个错误,或者说是 Ruby 1.9 的规格不足。请注意,Ruby 2.0 提出了一个

TypeError: class or module required for rescue clause

在第 8 行和第 14 行。

请注意,raise也不一定会像您认为的那样做。当你raise是一个对象时,你实际上并没有提出那个对象,你提出了一个对象,它是根据这些简单的规则从你传递的对象构造的:

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

所以,你实际上不是在加注x,而是在加注x.exception。但是,根据Exception#exception x.exceptionis的文档x

于 2012-12-04T20:07:16.737 回答
1

我只想在表格中添加一些内容:OP 代码表明这两个异常是相同的,但它们不是 - 此外,我想说明 OP 的含义:

所以看起来在做 raise 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}
于 2012-12-04T21:04:24.393 回答
0

编辑:为了澄清未来回复的问题,因为我认为我的回复不正确,这里的微妙之处在于xandyinstances而不是classes,通常你会在raise声明中使用 classes。


如果预期的行为是在第二次救援时打印,y则不会成功。您正在引发 class 异常x,并且您没有可以处理的救援子句x。如果你抓住StandardError了通用基类,你会看到第二个块打印了“ok”,但是:

begin
  raise x
rescue StandardError
  puts "ok"
end

关于#===,我认为问题在于当你提出时,你处理的是一个实例x不是x一个类。

于 2012-12-04T18:13:01.557 回答
0

看来救援的定义是:

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

严格来说也不x是. 它们是错误类型的实例。我认为您并没有真正按照您在那里的方式运行有效代码。yerror_type

如果你运行:

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

然后它将按预期工作。

另请注意,在 Ruby 1.8 上既不x == y也不x === y返回true

于 2012-12-04T18:25:01.563 回答