Ryan Davis的Ruby QuickRef说(没有解释):


不要挽救Exception。永远否则我会刺你。


为什么不呢?正确的做法是什么?

评论

那你可能可以自己写? :)

我对这里的暴力呼吁感到非常不自在。只是编程而已。

看看Ruby Exception中的这篇文章,它有一个很好的Ruby Exception层次结构。

因为瑞安·戴维斯(Ryan Davis)会刺你。孩子们永远不要挽救异常。

@DarthEgregious我真的无法分辨你是否在开玩笑。但我认为这很有趣。 (这显然不是严重的威胁)。现在,每当我想到捕获Exception时,我都会考虑是否值得在Internet上将某个随机的家伙刺伤。

#1 楼

TL; DR:使用StandardError代替常规异常捕获。重新引发原始异常时(例如,当救援仅记录异常时),救援Exception可能还可以。


Exception是Ruby异常层次结构的根,所以当您rescue Exception您可以从所有内容中进行救援,包括诸如SyntaxErrorLoadErrorInterrupt之类的子类。

救援Interrupt可以防止用户使用CTRLC退出程序。

救援SignalException可以防止程序不能正确响应信号。除kill -9之外,它将是不可杀死的。

抢救SyntaxError意味着失败的eval会无声地执行。

通过运行此程序并尝试显示所有这些信息。到CTRLC或kill

loop do
  begin
    sleep 1
    eval "djsakru3924r9eiuorwju3498 += 5u84fior8u8t4ruyf8ihiure"
  rescue Exception
    puts "I refuse to fail or be stopped!"
  end
end


Exception救援甚至都不是默认值。正在执行

begin
  # iceberg!
rescue
  # lifeboats
end


不是从Exception救援,而是从StandardError救援。通常,您应该指定比默认StandardError更具体的内容,但是从Exception抢救会扩大范围而不是缩小范围,并且可能会带来灾难性的结果,并且使错误查找极为困难。


如果如果您确实想从StandardError中解救出来,并且需要一个例外的变量,则可以使用以下形式:

begin
  # iceberg!
rescue => e
  # lifeboats
end


,它等效于:

begin
  # iceberg!
rescue StandardError => e
  # lifeboats
end



为从日志记录/报告目的而明智地从Exception救援的几种常见情况之一,在这种情况下,您应立即重新举起例外:

begin
  # iceberg?
rescue Exception => e
  # do some logging
  raise # not enough lifeboats ;)
end


评论


所以就像在Java中捕获Throwable

–棘轮怪胎
2012年4月7日,0:15

该建议对干净的Ruby环境很有用。但是不幸的是,许多宝石创造了直接源自Exception的异常。我们的环境中有30种: OpenID :: Server :: EncodingError,OAuth :: InvalidRequest,HTMLTokenizerSample。这些是您非常想在标准救援块中捕获的例外。不幸的是,Ruby中没有任何东西可以阻止甚至阻止宝石直接从Exception继承,甚至命名也不是很直观的。

–乔纳森·斯沃兹(Jonathan Swartz)
2013年9月19日17:08



@JonathanSwartz然后从那些特定的子类中抢救,而不是从Exception中抢救出来。越具体越好,总是越清晰越好。

–安德鲁·马歇尔(Andrew Marshall)
2013年9月19日19:15在

@JonathanSwartz-我会调试gem创造者,以更改其异常继承的内容。就个人而言,我希望我的宝石具有所有异常都来自MyGemException,因此,如果需要,您可以进行挽救。

–内森·朗(Nathan Long)
2014年1月31日22:19

您还可以ADAPTER_ERRORS = [:: ActiveRecord :: StatementInvalid,PGError,Mysql :: Error,Mysql2 :: Error,:: ActiveRecord :: JDBCError,SQLite3 :: Exception],然后营救* ADAPTER_ERRORS => e

– j_mcnally
2014-2-27在2:19

#2 楼

真正的规则是:不要丢弃异常。您的报价作者的客观性值得怀疑,事实以它结尾的是


,否则我会刺你


当然,请注意,信号(默认情况下)会引发异常,并且通常长时间运行的进程会通过信号终止,因此捕获Exception而不终止信号异常将使您的程序很难停止。所以不要这样做:

#! /usr/bin/ruby

while true do
  begin
    line = STDIN.gets
    # heavy processing
  rescue Exception => e
    puts "caught exception #{e}! ohnoes!"
  end
end


不,真的,不要这样做。甚至不要运行它来查看它是否有效。

但是,说您有一个线程服务器,并且希望所有异常都不要:


被忽略(默认设置)
停止服务器(如果您说thread.abort_on_exception = true,则会发生此情况)。

然后这在您的连接处理线程中是完全可以接受的:

begin
  # do stuff
rescue Exception => e
  myLogger.error("uncaught #{e} exception while handling connection: #{e.message}")
    myLogger.error("Stack trace: #{backtrace.map {|l| "  #{l}\n"}.join}")
end


以上内容是Ruby默认异常处理程序的一种变体,它不会杀死您程序的优点。 Rails在其请求处理程序中执行此操作。

主线程中引发了信号异常。后台线程不会获取它们,因此试图将它们捕获在那里是没有意义的。

这在生产环境中特别有用,在生产环境中,您不希望程序在发生任何事情时就简单地停止错误。然后,您可以将堆栈转储记录到日志中,并添加到代码中,以更优雅的方式在调用链的下方处理特定异常。

还要注意,还有另一个Ruby惯用语效果大致相同:

a = do_something rescue "something else"


在这一行中,如果do_something引发异常,它将被Ruby捕获并丢弃,并为a分配"something else"

通常,除了在不需要知道的特殊情况下,不要这样做。一个示例:

debugger rescue nil


debugger函数是在代码中设置断点的一种不错的方法,但是如果在调试器和Rails之外运行,则会引发异常。从理论上讲,现在您不应该将调试代码留在程序中(pff!没人这样做!),但是出于某种原因,您可能希望将其保留一段时间,但又不想继续运行调试器。

注意:



如果您运行了别人的程序来捕获信号异常并忽略了它们,(请说上面的代码)然后:
<在Linux的shell中,键入pgrep rubyps | grep ruby,查找有问题的程序的PID,然后运行kill -9 <PID>
在Windows中,使用任务管理器(CTRL-SHIFT-ESC),转到“进程”选项卡,找到您的进程,右键单击它并选择“结束进程”。



如果您正在使用别人的程序,无论出于何种原因,这些程序都充斥着这些ignore-exception块,那么将其放在主行顶部是一种可能的解决方案:

%W/INT QUIT TERM/.each { |sig| trap sig,"SYSTEM_DEFAULT" }


这将导致程序通过立即终止,绕过异常处理程序而不进行清除来响应正常终止信号。因此可能会导致数据丢失或类似情况。请小心!


如果需要执行以下操作:

begin
  do_something
rescue Exception => e
  critical_cleanup
  raise
end


您实际上可以执行以下操作:

begin
  do_something
ensure
  critical_cleanup
end


在第二种情况下,无论是否引发异常,都会每次调用critical cleanup



评论


抱歉,这是错误的。服务器永远都不能挽救Exception,而只能对其进行记录。这将使其无法杀死,除非被杀死-9。

–约翰
2012年4月8日在1:37

注释3中的示例并不恰当,无论是否引发异常,都将确保运行,而救援仅在引发异常时运行。

–安德鲁·马歇尔(Andrew Marshall)
2012年12月8日,0:13

它们不是/ exactly /等价的,但是我无法弄清楚如何以一种不丑陋的方式简洁地表达对等物。

–迈克尔·斯莱德
13年1月31日下午5:32

在第一个示例中,只需在begin / rescue块之后添加另一个critical_cleanup调用即可。我同意的不是最优雅的代码,但是显然第二个示例是优雅的实现方式,因此,一点点优雅只是该示例的一部分。

–gtd
13年3月6日在12:58

“甚至不要运行它来查看它是否有效。”相反,我建议您运行它,看看它是否失败,并亲自了解如果失败了,而不是盲目地相信别人。无论如何,很好的答案:)

– huelbois
16年11月9日在15:07

#3 楼

TL; DR
不要rescue Exception => e(并且不要重新引发异常)-否则您可能会开车过桥。

假设您正在开车(正在运行Ruby)。您最近使用空中升级系统(使用eval)安装了一个新的方向盘,但您不知道其中一位程序员对语法感到迷惑。
您正处在桥梁之中,意识到了正在向栏杆方向移动,所以您向左转。
def turn_left
  self.turn left:
end

糟糕!幸运的是,Ruby提出了一个SyntaxError
汽车应该立即停止-对吗?
不是。
begin
  #...
  eval self.steering_wheel
  #...
rescue Exception => e
  self.beep
  self.log "Caught #{e}.", :warn
  self.log "Logged Error - Continuing Process.", :info
end


哔哔声
警告:捕获到SyntaxError异常。
信息:记录的错误-继续进行过程。

您注意到出了点问题,并猛烈抨击紧急情况(^CInterrupt) />哔哔声
警告:捕获到中断异常。
信息:已记录错误-继续执行过程。

是的-并没有太大帮助。您非常靠近铁路,因此将汽车停在了停车场(kill ing:SignalException)。

哔哔声
警告:捕获到SignalException异常。
信息:已记录错误-继续过程。

最后一秒钟,您拔出钥匙(kill -9),汽车停下来,您猛烈地撞向方向盘(安全气囊无法充气,因为您没有t正常停止该程序-您将其终止),然后汽车后部的计算机将其撞到其前面的座椅上。半满的可乐罐溢出了报纸。后面的杂物被压碎,大部分被蛋黄和牛奶覆盖。该车需要认真维修和清洁。 (数据丢失)
希望您有保险(备份)。哦,是的-由于安全气囊没有膨胀,您可能受到了伤害(被开除等)。

但是请稍等!还有更多原因可能使您想使用rescue Exception => e
假设您是那辆车,并且要确保在汽车超过其安全停止动量时安全气囊会充气。
 begin 
    # do driving stuff
 rescue Exception => e
    self.airbags.inflate if self.exceeding_safe_stopping_momentum?
    raise
 end

这里是规则的例外:只有重新引发例外时,您才能捕获Exception。因此,更好的规则是永远不要吞咽Exception,并且总是重新引发错误。
但是在Ruby之类的语言中添加救援既容易忘记,又可以在重新提出问题之前放下救援声明有点不干。而且您也不想忘记raise语句。如果您愿意,可以尝试找到该错误,祝您好运。
幸运的是,Ruby非常棒,您可以使用ensure关键字来确保代码能够运行。无论如何,关键字ensure都将运行代码-如果引发异常,如果没有引发异常,则唯一的例外就是世界终结(或其他不太可能的事件)。
 begin 
    # do driving stuff
 ensure
    self.airbags.inflate if self.exceeding_safe_stopping_momentum?
 end

!而且该代码无论如何都应该运行。应该使用rescue Exception => e的唯一原因是,如果您需要访问异常,或者仅希望代码在异常上运行。并记住要重新提出该错误。每次。
注意:正如@Niall指出的那样,请确保始终运行。这是件好事,因为有时即使您遇到问题,您的程序也可能撒谎,不会抛出异常。对于重要的任务,例如给安全气囊充气,您需要确保无论发生什么事情都会发生。因此,每次停车时检查是否抛出异常都是一个好主意。尽管在大多数编程情况下,安全气囊的充气并不常见,但实际上在大多数清理任务中还是很常见。

评论


哈哈哈哈!这是一个很好的答案。我很震惊,没有人发表评论。您给出了一个清晰的场景,使整个事情真正变得可以理解。干杯! :-)

–詹姆斯·米兰尼(James Milani)
17年7月20日在19:10

@JamesMilani谢谢!

–本·奥宾
17年7月21日在21:14



+💯这个答案。希望我能投票一次以上! 😂

–engineerDave
17-10-5在16:58

喜欢你的答案!

– Atul Vaibhav
17年11月24日在7:28



这个答案是在完全可以理解和正确接受的答案之后的四年,并以一种荒谬的场景重新解释了这个场景,该场景设计得比现实更有趣。抱歉,成为嗡嗡声,但这不是Reddit-答案简洁,正确而不是有趣更重要。另外,关于确保作为挽救异常的替代方法的部分具有误导性-该示例暗示它们是等效的,但如上所述,确保无论是否存在异常都将发生,所以现在您的安全气囊会膨胀,因为即使超过5英里/小时,您的速度超过5英里/小时错误。

–尼尔
18年7月31日在12:37

#4 楼

因为这捕获了所有异常。您的程序不太可能从其中任何一个恢复。

您应该只处理知道如何恢复的异常。如果您不希望发生某种异常,请不要处理它,大声崩溃(将详细信息写入日志),然后诊断日志并修复代码。

吞咽异常是不好的,不要做到这一点。

#5 楼

这是规则的一个特例,您不应捕获任何您不知道如何处理的异常。如果您不知道如何处理它,最好让系统的其他部分捕获并处理它。

#6 楼

这篇博客文章对它进行了完美的解释:
Ruby的Exception与StandardError:有什么区别?

为什么不应该拯救Exception
抢救Exception
的问题是实际上,它可以挽救从
Exception继承的每个异常。这是....所有人!
这是一个问题,因为Ruby内部使用了一些例外情况。它们与您的应用程序没有任何关系,
吞咽它们会导致不好的事情发生。
以下是其中的一些大问题:


SignalException :: Interrupt-如果要挽救此错误,则无法通过按Control-c退出
应用。


ScriptError :: SyntaxError-吞咽语法错误意味着
像puts(“ Forgot something”)会静默失败。


NoMemoryError-想知道当程序用完所有RAM后保持运行时会发生什么情况我也不是。


 begin
  do_something()
rescue Exception => e
  # Don't do this. This will swallow every single exception. Nothing gets past it. 
end
 

我猜你真的不想吞下所有这些
系统级异常。您只想捕获所有
应用程序级错误。这些异常导致了您的代码。
幸运的是,有一种简便的方法可以解决此问题。 br />挽救StandardError代替
您应该关心的所有例外均从StandardError继承。这些是我们的老朋友: NoMethodError-当您尝试调用不存在的方法时引发
TypeError-由1 +“”
RuntimeError-谁会忘记旧的RuntimeError引起的事情?
要挽救此类错误,您将需要挽救StandardError。您可以通过编写以下代码来做到这一点:
 begin
  do_something()
rescue StandardError => e
  # Only your app's exceptions are swallowed. Things like SyntaxErrror are left alone. 
end
 

但是Ruby使它更易于使用。
当您完全不指定异常类时,ruby假定您的意思是StandardError。因此,下面的代码与上面的代码相同:
 begin
  do_something()
rescue => e
  # This is the same as rescuing StandardError
end