Go语言的创造者写道:
Go doesn't provide assertions. They are undeniably convenient, but our experience has been that programmers use them as a crutch to avoid thinking about proper error handling and reporting. Proper error handling means that servers continue operation after non-fatal errors instead of crashing. Proper error reporting means that errors are direct and to the point, saving the programmer from interpreting a large crash trace. Precise errors are particularly important when the programmer seeing the errors is not familiar with the code.
你对此有什么看法?
断言并不邪恶,但很容易被滥用。我非常同意“断言经常被用作一种拐杖,以避免考虑正确的错误处理和报告”的说法。我经常看到这种情况。
就我个人而言,我确实喜欢使用断言,因为它们记录了我在编写代码时可能做出的假设。如果在维护代码时这些假设被打破了,那么可以在测试期间检测到问题。但是,在进行产品构建时(即使用#ifdefs),我确实强调要从代码中剥离出每个断言。通过剥离产品构建中的断言,我消除了任何人将其误用为拐杖的风险。
断言还有另一个问题。断言只在运行时进行检查。但是通常情况下,您想要执行的检查可以在编译时执行。最好在编译时检测问题。对于c++程序员,boost提供了BOOST_STATIC_ASSERT,它允许您执行此操作。对于C程序员,本文(链接文本)描述了一种可用于在编译时执行断言的技术。
总之,我遵循的经验法则是:不要在生产构建中使用断言,如果可能的话,只对在编译时无法验证的东西使用断言(即,必须在运行时检查)。
我最近开始在我的代码中添加一些断言,这是我一直在做的:
我在心里把代码分为边界代码和内部代码。边界代码是处理用户输入、读取文件和从网络获取数据的代码。在这段代码中,我在一个循环中请求输入,该循环仅在输入有效时退出(在交互式用户输入的情况下),或者在不可恢复的文件/网络损坏数据的情况下抛出异常。
内部代码就是一切。例如,在我的类中设置变量的函数可以定义为
void Class::f (int value) {
assert (value < end);
member = value;
}
从网络获取输入的函数可以这样读:
void Class::g (InMessage & msg) {
int const value = msg.read_int();
if (value >= end)
throw InvalidServerData();
f (value);
}
This gives me two layers of checks. Anything where the data is determined at run-time always gets an exception or immediate error handling. However, that extra check in Class::f with the assert statement means that if some internal code ever calls Class::f, I still have a sanity check. My internal code might not pass a valid argument (because I may have calculated value from some complex series of functions), so I like having the assertion in the setting function to document that regardless of who is calling the function, value must not be greater than or equal to end.
这似乎符合我在一些地方读到的内容,即在一个功能良好的程序中,断言应该是不可能违反的,而例外应该是针对仍然可能发生的异常和错误情况。因为理论上我要验证所有输入,所以我的断言不应该被触发。如果是,我的程序就错了。
Assert非常有用,可以在出现意外错误时通过在出现问题的第一个迹象时停止程序来节省大量回溯时间。
另一方面,断言很容易被滥用。
int quotient(int a, int b){
assert(b != 0);
return a / b;
}
正确的说法应该是:
bool quotient(int a, int b, int &result){
if(b == 0)
return false;
result = a / b;
return true;
}
所以…从长远来看……从大局来看……我必须同意断言可能会被滥用。我一直都这么做。