在 Ruby 中引发异常与抛出异常有啥区别?
Posted
技术标签:
【中文标题】在 Ruby 中引发异常与抛出异常有啥区别?【英文标题】:What is the difference between Raising Exceptions vs Throwing Exceptions in Ruby?在 Ruby 中引发异常与抛出异常有什么区别? 【发布时间】:2010-09-08 05:43:35 【问题描述】:Ruby 有两种不同的异常机制:Throw/Catch 和 Raise/Rescue。
为什么我们有两个?
什么时候应该使用一个而不是另一个?
【问题讨论】:
“摆脱嵌套循环”是许多编程语言的共同需求。除了@docwhat 提到的C/C++ 中的goto
,Java 还有labeled break and continue。 (Python 也为此提供了 rejected proposal。)
【参考方案1】:
raise
、fail
、rescue
和 ensure
处理错误,也称为异常
throw
和 catch
是控制流
不同于其他 语言中,Ruby 的 throw 和 catch 不用于异常。 相反,它们提供了一种在没有执行的情况下提前终止执行的方法 需要做进一步的工作。 (格林,2011)
可以使用简单的return
来终止单个级别的控制流,例如while
循环。可以使用throw
终止多个级别的控制流,例如嵌套循环。
虽然 raise 和 rescue 的异常机制非常适合在出现问题时放弃执行,但有时能够在正常处理期间跳出一些深度嵌套的构造也很不错。这就是 catch 和 throw 派上用场的地方。 (托马斯和亨特,2001)
参考文献
-
格林,阿夫迪。 “投、接、举、救……我好糊涂!” Ruby 学习博客。 N.p.,2011 年 7 月 11 日。网络。 2012 年 1 月 1 日。http://rubylearning.com/blog/2011/07/12/throw-catch-raise-rescue--im-so-confused/。
托马斯、戴夫和安德鲁亨特。 “编程红宝石。” : 实用程序员指南。 N.p.,2001 年。网络。 2015 年 9 月 29 日。http://ruby-doc.com/docs/ProgrammingRuby/html/tut_exceptions.html。
【讨论】:
Avdi 确实不看起来像他在播客中的声音。 Ruby 学习链接似乎不起作用。这是另一篇讨论差异的博客文章:danielchangnyc.github.io/blog/2013/10/23/throw-raise 有趣的是,rubylearning.com 认为 Avdi 的文章 is still there。我想这就是我们将东西复制到 SO 的原因,所以它不会丢失!【参考方案2】:我认为http://hasno.info/ruby-gotchas-and-caveats 对差异有很好的解释:
catch/throw 与 raise/rescue 不同。 catch/throw 允许您快速退出块,回到为特定符号定义 catch 的点,引发救援是涉及 Exception 对象的真正异常处理内容。
【讨论】:
很想知道...从 iPad 上阅读此内容,因此无法在 1.9 中对其进行测试,但其中一些陷阱在最近的 ruby 版本中不再有效,对吧? 另外值得知道:raise
很贵。 throw
不是。将throw
视为使用goto
来摆脱循环。
@Denis 你指的是哪个陷阱?
链接失效了!
请参阅ruby catch-throw and efficiency 以了解有关性能差异的更多信息。【参考方案3】:
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
”行为对您正在编写的算法有意义但考虑@987654338 没有意义@ 对应于错误条件。
What is catch and throw used for in Ruby? 提供了一些关于throw
/catch
构造的良好用法的建议。
它们之间的具体行为差异包括:
rescue Foo
将拯救Foo
的实例,包括Foo
的子类。 catch(foo)
只会捕获同一个对象,Foo
。你不仅不能传递catch
一个类名来捕获它的实例,而且它甚至不会进行相等比较。比如
catch("foo") do
throw "foo"
end
会给你一个UncaughtThrowError: uncaught throw "foo"
(或者在 Ruby 2.2 之前的版本中是一个ArgumentError
)
可以列出多个救援子句...
begin
do_something_error_prone
rescue AParticularKindOfError
# Insert heroism here.
rescue
write_to_error_log
raise
end
虽然需要嵌套多个catch
es...
catch :foo do
catch :bar do
do_something_that_can_throw_foo_or_bar
end
end
裸 rescue
等同于 rescue StandardError
并且是惯用的构造。像catch() throw :foo
这样的“裸catch
”永远不会捕获任何东西,也不应该使用。
【讨论】:
很好的解释,但引出了一个问题,他们到底为什么要用 ruby 设计 raise = throw 用其他语言。然后还包括 throw 但它 != throw 其他语言。我在那里看不到他们的原始逻辑 @wired00 (耸耸肩。) 我同意它与当今其他流行语言相比显得相当古怪。 @wired00:自从 1960 年代第一次尝试结构化错误处理以来,它就被称为“引发”异常,在发明现代技术的开创性文章中,它被称为“引发”异常异常处理的形式,在 Lisps 和 Smalltalks 中被称为“引发”异常,这是 Ruby 的一些主要灵感来源,在硬件中被称为“引发”异常或“引发”中断甚至在“编程语言”的概念出现之前。问题应该是:为什么其他语言会改变这一点? @MarkAmery:请记住,许多“其他流行语言”比 Ruby年轻 或者至少是现代的。所以,问题应该是:为什么那些其他语言不遵循 Ruby(以及 Smalltalk 和 Lisp 以及硬件和文献)。 @JörgWMittag 有趣 - 你启发了我做一些历史研究。在 Ruby 出现之前几年,C++ 就有“抛出”异常的概念,而根据english.stackexchange.com/a/449209/73974,这个术语实际上可以追溯到 70 年代……所以我认为我们仍然会批评 Ruby 采用既定术语并用它来表示完全不同的东西。以上是关于在 Ruby 中引发异常与抛出异常有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章