是断言邪恶?

Fra*_*ank 195 c c++ error-handling assert go

Go语言的创造者写的:

Go不提供断言.它们无疑是方便的,但我们的经验是程序员将它们用作拐杖以避免考虑正确的错误处理和报告.正确的错误处理意味着服务器在非致命错误而不是崩溃后继续运行.正确的错误报告意味着错误是直接的,并且可以避免程序员解释大的崩溃跟踪.当程序员看到错误时不熟悉代码时,精确错误尤为重要.

你对此有何看法?

caf*_*caf 318

不,assert只要您按预期使用它就没有任何问题.

也就是说,它应该用于在调试期间捕获"不可能发生"的情况,而不是正常的错误处理.

  • 断言:程序逻辑本身失败.
  • 错误处理:错误的输入或系统状态不是由程序中的错误引起的.


gah*_*ooa 106

不,既不是goto也不assert邪恶.但两者都可能被滥用.

断言是为了进行健全性检查.如果不正确,应该杀死程序的东西.不用于验证或替代错误处理.

  • @ar2015 找到一些人出于纯粹宗教原因建议避免使用 `goto` 的荒谬设计模式之一,然后使用 `goto` 而不是混淆你正在做的事情。换句话说:如果你能证明你真的需要`goto`,而唯一的选择是制定一堆毫无意义的脚手架,最终做同样的事情但不改变Goto Police......那么只需使用`goto`。当然,这样做的前提是“如果你能证明你真的需要`goto`”。通常,人们不会。这仍然并不意味着它本质上是一件坏事。 (3认同)
  • `goto`在linux内核中用于代码清理 (2认同)

jal*_*alf 61

按照这种逻辑,断点也是邪恶的.

断言应该用作调试辅助,而不是其他任何东西."邪恶"是指您尝试使用它们而不是错误处理.

断言可以帮助您,程序员,检测和修复不存在的问题,并验证您的假设是否成立.

它们与错误处理无关,但不幸的是,一些程序员滥用它们,然后宣称它们是"邪恶的".


arh*_*aco 39

我喜欢断言很多.当我第一次构建应用程序时(或许对于新域),我发现它非常有用.而不是做非常花哨的错误检查(我会考虑过早优化)我快速编码,我添加了很多断言.在我更了解事情是如何工作之后,我会重写并删除一些断言并更改它们以便更好地处理错误.

由于断言,我花了很多时间编写/调试程序.

我也注意到这些断言帮助我想到许多可能破坏我程序的事情.


Ale*_*ski 30

它们应该用于检测程序中的错误.用户输入不错.

如果使用得当,它们不是邪恶的.


Ran*_*ku' 30

作为附加信息,go提供了内置功能panic.这可以用来代替assert.例如

if x < 0 {
    panic("x is less than 0");
}
Run Code Online (Sandbox Code Playgroud)

panic将打印堆栈跟踪,因此在某种程度上它的目的是assert.


jto*_*lle 13

这出现了很多,我认为一个使断言防御混乱的问题是它们通常基于参数检查.因此,请考虑使用断言时的不同示例:

build-sorted-list-from-user-input(input)

    throw-exception-if-bad-input(input)

    ...

    //build list using algorithm that you expect to give a sorted list

    ...

    assert(is-sorted(list))

end
Run Code Online (Sandbox Code Playgroud)

您对输入使用异常,因为您希望有时会得到错误的输入.您声明列表已排序,以帮助您找到算法中的错误,根据定义,您不会发现该错误.断言仅在调试版本中,因此即使检查很昂贵,您也不介意在每次调用例程时都这样做.

您仍然需要对生产代码进行单元测试,但这是确保代码正确的另一种补充方法.单元测试确保您的例程符合其界面,而断言是一种更精细的方式,以确保您的实现完全符合您的预期.


fig*_*ssa 8

断言不是邪恶的,但很容易被滥用.我同意这样的说法:"断言经常被用作拐杖,以避免考虑正确的错误处理和报告".我经常看到这个.

就个人而言,我喜欢使用断言,因为它们记录了我在编写代码时可能做出的假设.如果在维护代码时破坏了这些假设,则可以在测试期间检测到问题.但是,我确实在进行生产构建时(即使用#ifdefs)从我的代码中删除每个断言.通过剥离生产构建中的断言,我消除了任何人滥用它们作为拐杖的风险.

断言还有另一个问题.断言仅在运行时检查.但通常情况下,您希望执行的检查可能是在编译时执行的.最好在编译时检测问题.对于C++程序员,boost提供了BOOST_STATIC_ASSERT,允许您执行此操作.对于C程序员,本文(链接文本)描述了一种可用于在编译时执行断言的技术.

总之,我遵循的经验法则是:不要在生产构建中使用断言,并且如果可能的话,只对在编译时无法验证的事物使用断言(即,必须在运行时检查).


Sta*_*ked 5

我承认在没有考虑正确的错误报告的情况下使用了断言.但是,如果使用得当,它们会非常有用.

如果您想遵循"早期崩溃"原则,它们尤其有用.例如,假设您正在实现引用计数机制.在代码中的某些位置,您知道引用计数应为零或一.并且假设如果引用计数错误,程序将不会立即崩溃,但在下一个消息循环期间,将很难找出出错的原因.断言有助于检测更接近其原点的错误.


New*_*ian 5

我不喜欢强烈的断言.我不会说他们是邪恶的.

基本上断言将执行与未经检查的异常相同的事情,唯一的例外是不应该为最终产品保留断言(通常).

如果您在调试和构建系统时为自己构建安全网,为什么要拒​​绝为您的客户,支持服务台或任何将使用您当前正在构建的软件的安全网.仅对断言和异常情况使用异常.通过创建适当的异常层次结构,您将能够非常快速地识别出另一个异常层次结构.除此之外,断言仍然存在,并且可以在失败的情况下提供有价值的信息,否则将丢失.

因此,我通过完全删除断言并强制程序员使用异常来处理这种情况,完全理解Go的创建者.对此有一个简单的解释,例外只是一个更好的机制,为什么坚持古老的断言?


Pav*_*sky 5

我更喜欢避免在调试和发布中执行不同操作的代码.

在一个条件下打破调试器并且所有文件/行信息都很有用,但确切的表达式和确切的值也是如此.

具有"仅在调试中评估条件"的断言可能是性能优化,因此仅在0.0001%的程序中有用 - 人们知道他们在做什么.在所有其他情况下,这是有害的,因为表达式实际上可能改变程序的状态:

assert(2 == ShroedingersCat.GetNumEars()); 会使程序在调试和发布中做不同的事情.

我们开发了一组断言宏,它会引发异常,并在调试版和发行版中都这样做.例如,THROW_UNLESS_EQ(a, 20);会抛出具有文件,行和a的实际值的what()消息的异常,依此类推.只有宏才能拥有这种力量.调试器可以配置为在特定异常类型的'throw'处中断.

  • 参数中使用的统计数据的90%是错误的. (4认同)