Ruby Exceptions - 为什么“else”?

问题描述 投票:48回答:5

我试图理解Ruby中的异常,但我有点困惑。我正在使用的教程说,如果发生的异常与rescue语句识别的任何异常都不匹配,您可以使用“else”来捕获它:

begin  
# -  
rescue OneTypeOfException  
# -  
rescue AnotherTypeOfException  
# -  
else  
# Other exceptions
ensure
# Always will be executed
end

但是,我在后面的教程“救援”中也看到了没有指定的异常:

begin
    file = open("/unexistant_file")
    if file
         puts "File opened successfully"
    end
rescue
    file = STDIN
end
print file, "==", STDIN, "\n"

如果你能做到这一点,那么我是否需要使用其他?或者我可以像这样在最后使用通用救援?

begin  
# -  
rescue OneTypeOfException  
# -  
rescue AnotherTypeOfException  
# -  
rescue
# Other exceptions
ensure
# Always will be executed
end
ruby exception-handling
5个回答
89
投票

else用于块完成时没有抛出异常。无论块是否成功完成,都会运行ensure。例:

begin
  puts "Hello, world!"
rescue
  puts "rescue"
else
  puts "else"
ensure
  puts "ensure"
end

这将打印Hello, world!,然后else,然后ensure


5
投票

这是else表达式中begin的具体用例。假设您正在编写自动化测试,并且您想编写一个返回块引发的错误的方法。但是如果块没有引发错误,您还希望测试失败。你可以这样做:

def get_error_from(&block)
  begin
    block.call
  rescue => err
    err  # we want to return this
  else
    raise "No error was raised"
  end
end

请注意,您无法在raise块内移动begin,因为它将获得rescued。当然,还有其他方法不使用else,比如在err之后检查nil是否是end,但这并不简洁。

就个人而言,我很少以这种方式使用else,因为我认为它很少需要,但在极少数情况下确实派上用场。

编辑

另一个用例发生在我身上。这是一个典型的begin / rescue

begin
  do_something_that_may_raise_argument_error
  do_something_else_when_the_previous_line_doesnt_raise
rescue ArgumentError => e
  handle_the_error
end

为什么这不太理想?因为当rescue提升do_something_that_may_raise_argument_error时,意图是ArgumentError,而不是当do_something_else_when_the_previous_line_doesnt_raise提升时。

通常最好使用begin / rescue来包装你想要保护的最小代码以免raise,否则:

  • 你可以掩盖不应该raise的代码中的错误
  • rescue的意图更难破译。有人(包括你未来的自己)可能会阅读代码并想知道“我想保护哪个表达?它看起来像表达ABC ......但也许表达DEF ????作者想要什么?!”重构变得更加困难。

通过这个简单的更改可以避免这些问题:

begin
  do_something_that_may_raise_argument_error
rescue ArgumentError => e
  handle_the_error
else
  do_something_else_when_the_previous_line_doesnt_raise
end

1
投票

当您可能期望发生某种异常时,会使用开始执行结束块中的else块。如果你运行了所有预期的异常但仍然没有引发任何异常,那么在你的else块中你可以做任何你需要的事情,因为你知道你的原始代码没有运行。


0
投票

我可以看到else块的唯一原因是,如果你想在ensure块之前执行某些操作,那么begin块中的代码不会引发任何错误。

begin
  puts "Hello"
rescue
  puts "Error"
else
  puts "Success"
ensure
  puts "my old friend"
  puts "I've come to talk with you again."
end

0
投票

感谢else,你有时可以合并两个嵌套的begin end块。 所以(从我当前的代码简化示例)而不是:

  begin
    html = begin
      NetHTTPUtils.request_data url
    rescue NetHTTPUtils::Error => e
      raise unless 503 == e.code
      sleep 60
      retry
    end
    redo unless html["market"]
  end

你写:

  begin
    html = NetHTTPUtils.request_data url
  rescue NetHTTPUtils::Error => e
    raise unless 503 == e.code
    sleep 60
    retry
  else
    redo unless html["market"]
  end
© www.soinside.com 2019 - 2024. All rights reserved.