在 Ruby 中失败与加注:我们真的应该相信风格指南吗?

Fail vs. raise in Ruby : Should we really believe the style guide?

Ruby 提供了两种以编程方式引起异常的可能性:raisefail,两者都是 Kernel 方法。根据文件,它们是绝对等价的。

出于习惯,到目前为止我只使用了raise。现在我发现了一些建议(例如 here),使用 raise 来捕获异常,使用 fail 来处理不应该处理的严重错误。

但这真的有意义吗?当您编写 class 或模块,并在内部引发问题时,您通过 fail 发出信号,正在审查代码的编程同事可能会很高兴地理解您的意图,但是负责的人使用 我的代码很可能不会查看我的代码并且无法知道异常是由 raise 还是 fail 引起的。所以,我小心翼翼地使用raisefail,并不能影响他的决定,她该不该处理。

有人能看出我论点中的缺陷吗?或者还有其他标准,我可能想使用 fail 而不是 raise

use 'raise' for exceptions to be caught, and 'fail' for serious errors which are not meant to be handled

这不是您提供的 official style guide 或 link 所说的。

这里的意思是只在rescue块中使用raise。也就是当你想说某事 失败 时使用 fail,当 重新抛出 异常时使用 raise

至于 "does it matter" 部分 - 它不是最严格遵守的规则之一,但您可以对任何约定提出相同的论点。你应该按照这个顺序:

  1. 您的项目风格指南
  2. 贵公司风格指南
  3. 社区风格指南

理想情况下,三者应该是一样的。


更新:截至this PR(2015 年 12 月),惯例是始终使用 raise.

我曾经和 Jim Weirich 谈过这件事,从那以后我总是使用 fail 当我的方法由于某种原因明显失败并且 raise 重新抛出例外。

这是一条 post 来自 Jim 的消息(几乎是逐字记录他亲自对我说的话): http://www.virtuouscode.com/2014/05/21/jim-weirich-on-exceptions/

这里是来自 post 的相关文本,引自 Jim:

Here’s my basic philosophy (and other random thoughts) on exceptions.

When you call a method, you have certain expectations about what the method will accomplish. Formally, these expectations are called post-conditions. A method should throw an exception whenever it fails to meet its postconditions.

To effectively use this strategy, it implies you must have a small understanding of Design by Contract and the meaning of pre- and post-conditions. I think that’s a good thing to know anyways.

Here’s some concrete examples. The Rails model save method:

model.save!
-- post-condition: The model object is saved.

If the model is not saved for some reason, then an exception must be raised because the post-condition is not met.

model.save
-- post-condition: (the model is saved && result == true) ||
                   (the model is not saved && result == false)

If save doesn’t actually save, then the returned result will be false , but the post-condition is still met, hence no exception.

I find it interesting that the save! method has a vastly simpler post-condition.

On the topic of rescuing exceptions, I think an application should have strategic points where exceptions are rescued. There is little need for rescue/rethrows for the most part. The only time you would want to rescue and rethrow is when you have a job half-way done and you want to undo something so avoid a partially complete state. Your strategic rescue points should be chosen carefully so that the program can continue with other work even if the current operation failed. Transaction processing programs should just move on to the next transaction. A Rails app should recover and be ready to handle the next http request.

Most exception handlers should be generic. Since exceptions indicate a failure of some type, then the handler needs only make a decision on what to do in case of failure. Detailed recovery operations for very specific exceptions are generally discouraged unless the handler is very close (call graph wise) to the point of the exception.

Exceptions should not be used for flow control, use throw/catch for that. This reserves exceptions for true failure conditions.

(An aside, because I use exceptions to indicate failures, I almost always use the fail keyword rather than the raise keyword in Ruby. Fail and raise are synonyms so there is no difference except that fail more clearly communcates that the method has failed. The only time I use raise is when I am catching an exception and re-raising it, because here I’m not failing, but explicitly and purposefully raising an exception. This is a stylistic issue I follow, but I doubt many other people do).

There you have it, a rather rambling memory dump on my thoughts on exceptions.

我知道有很多风格指南不同意(例如 style guide used by RoboCop)。我不在乎。吉姆说服了我。