Ruby 有两种不同的异常机制:Throw/Catch 和 Raise/Rescue。
为什么我们有两个?
什么时候应该使用其中一种而不是另一种?
raise
、fail
、rescue
和ensure
处理错误,也称为异常throw
和 catch
是 控制流与其他不同 语言中,Ruby 的 throw 和 catch 不用于异常。 相反,它们提供了一种在没有任何结果时提前终止执行的方法。 还需要进一步的工作。 (格林,2011)
终止单级控制流(如
while
循环)可以通过简单的 return
来完成。可以使用 throw
来终止多个级别的控制流(例如嵌套循环)。
虽然raise和rescue的异常机制对于在出现问题时放弃执行非常有用,但有时在正常处理期间能够跳出某些深度嵌套的构造也很好。这就是 catch 和 throw 派上用场的地方。 (托马斯和亨特,2001)
我认为http://hasno.info/ruby-gotchas-and-caveats对差异有一个很好的解释:
捕捉/投掷与举起/救援不同。 catch / throw允许您快速退出块回到为特定符号定义catch的点,引发救援是涉及Exception对象的真正异常处理内容。
https://coderwall.com/p/lhkkug/don-t-confuse-ruby-s- throw-statement-with-raise提供了一个很好的解释,我怀疑我可以改进。总而言之,我从博客文章中摘录了一些代码示例:
raise
/rescue
是与您熟悉的其他语言中的 throw
/catch
结构(或 Python 的 raise
/except
)最接近的类似物。如果您遇到错误情况并且您会在其他语言中 throw
解决它,那么在 Ruby 中您应该 raise
。Ruby 的
throw
/catch
可以让你中断执行并爬上堆栈寻找 catch
(就像 raise
/rescue
那样),但实际上并不是针对错误情况。它应该很少使用,并且只是在“沿着堆栈向上查找直到找到相应的 catch
”行为对于您正在编写的算法有意义但考虑 throw
没有意义时才存在。
对应于错误条件。
catch 和 throw 在 Ruby 中的用途是什么? 提供了一些关于
throw
/catch
结构的良好使用的建议。他们之间的具体行为差异包括:
rescue Foo
将拯救 Foo
的实例,包括 Foo
的子类。 catch(foo)
只会捕获 同一个对象,Foo
。您不仅不能传递 catch
类名来捕获它的实例,而且它甚至不会进行相等比较。比如说
catch("foo") do
throw "foo"
end
会给你一个
UncaughtThrowError: uncaught throw "foo"
(或者 2.2 之前的 Ruby 版本中的 ArgumentError
)可列出多个救援条款...
begin
do_something_error_prone
rescue AParticularKindOfError
# Insert heroism here.
rescue
write_to_error_log
raise
end
同时需要嵌套多个
catch
...
catch :foo do
catch :bar do
do_something_that_can_throw_foo_or_bar
end
end
裸露的
rescue
相当于rescue StandardError
,并且是一个惯用结构。 “裸露的 catch
”,如 catch() {throw :foo}
,永远不会捕获任何东西,因此不应该使用。