为什么在 Ruby 中使用 `rescue Exception => e` 样式不好?

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

不要挽救异常。永远不然我会刺你

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

答案

loop do
  begin
    sleep 1
    eval "djsakru3924r9eiuorwju3498 += 5u84fior8u8t4ruyf8ihiure"
  rescue Exception
    puts "I refuse to fail or be stopped!"
  end
end
begin
  # iceberg!
rescue
  # lifeboats
end
begin
  # iceberg!
rescue => e
  # lifeboats
end
begin
  # iceberg!
rescue StandardError => e
  # lifeboats
end
begin
  # iceberg?
rescue Exception => e
  # do some logging
  raise # not enough lifeboats ;)
end
#! /usr/bin/ruby

while true do
  begin
    line = STDIN.gets
    # heavy processing
  rescue Exception => e
    puts "caught exception #{e}! ohnoes!"
  end
end
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
a = do_something rescue "something else"
debugger rescue nil
def turn_left
  self.turn left:
end
begin
  #...
  eval self.steering_wheel
  #...
rescue Exception => e
  self.beep
  self.log "Caught #{e}.", :warn
  self.log "Logged Error - Continuing Process.", :info
end
begin 
    # do driving stuff
 rescue Exception => e
    self.airbags.inflate if self.exceeding_safe_stopping_momentum?
    raise
 end
begin 
    # do driving stuff
 ensure
    self.airbags.inflate if self.exceeding_safe_stopping_momentum?
 end

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

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

吞咽异常是不好的,不要这样做。

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

我刚刚在honeybadger.io 上阅读了一篇很棒的博客文章:

Ruby 的 Exception 与 StandardError:有什么区别?

为什么你不应该拯救异常

抢救 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

我猜您真的不想吞下任何这些系统级异常。您只想捕获所有应用程序级错误。异常导致您的代码。

幸运的是,有一个简单的方法可以做到这一点。