在 Ruby 中失败与加注:我们真的应该相信风格指南吗?
Fail vs. raise in Ruby : Should we really believe the style guide?
Ruby 提供了两种以编程方式引起异常的可能性:raise
和 fail
,两者都是 Kernel
方法。根据文件,它们是绝对等价的。
出于习惯,到目前为止我只使用了raise
。现在我发现了一些建议(例如 here),使用 raise
来捕获异常,使用 fail
来处理不应该处理的严重错误。
但这真的有意义吗?当您编写 class 或模块,并在内部引发问题时,您通过 fail
发出信号,正在审查代码的编程同事可能会很高兴地理解您的意图,但是负责的人使用 我的代码很可能不会查看我的代码并且无法知道异常是由 raise
还是 fail
引起的。所以,我小心翼翼地使用raise
或fail
,并不能影响他的决定,她该不该处理。
有人能看出我论点中的缺陷吗?或者还有其他标准,我可能想使用 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" 部分 - 它不是最严格遵守的规则之一,但您可以对任何约定提出相同的论点。你应该按照这个顺序:
- 您的项目风格指南
- 贵公司风格指南
- 社区风格指南
理想情况下,三者应该是一样的。
更新:截至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)。我不在乎。吉姆说服了我。
Ruby 提供了两种以编程方式引起异常的可能性:raise
和 fail
,两者都是 Kernel
方法。根据文件,它们是绝对等价的。
出于习惯,到目前为止我只使用了raise
。现在我发现了一些建议(例如 here),使用 raise
来捕获异常,使用 fail
来处理不应该处理的严重错误。
但这真的有意义吗?当您编写 class 或模块,并在内部引发问题时,您通过 fail
发出信号,正在审查代码的编程同事可能会很高兴地理解您的意图,但是负责的人使用 我的代码很可能不会查看我的代码并且无法知道异常是由 raise
还是 fail
引起的。所以,我小心翼翼地使用raise
或fail
,并不能影响他的决定,她该不该处理。
有人能看出我论点中的缺陷吗?或者还有其他标准,我可能想使用 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" 部分 - 它不是最严格遵守的规则之一,但您可以对任何约定提出相同的论点。你应该按照这个顺序:
- 您的项目风格指南
- 贵公司风格指南
- 社区风格指南
理想情况下,三者应该是一样的。
更新:截至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 befalse
, 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 theraise
keyword in Ruby.Fail
andraise
are synonyms so there is no difference except thatfail
more clearly communcates that the method has failed. The only time I useraise
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)。我不在乎。吉姆说服了我。