在 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】: raisefailrescueensure 处理错误,也称为异常 throwcatch控制流

不同于其他 语言中,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

虽然需要嵌套多个catches...

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 中引发异常与抛出异常有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

返回错误消息与抛出异常

异常捕获处理与抛出

Java中的异常的捕获和抛出是啥意思,有啥区别

Java的throw 和throws有啥区别?

Java中throw和throws有啥区别?

在锁 c#2 中抛出异常