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.

你对此有什么看法?


当前回答

我最近开始在我的代码中添加一些断言,这是我一直在做的:

我在心里把代码分为边界代码和内部代码。边界代码是处理用户输入、读取文件和从网络获取数据的代码。在这段代码中,我在一个循环中请求输入,该循环仅在输入有效时退出(在交互式用户输入的情况下),或者在不可恢复的文件/网络损坏数据的情况下抛出异常。

内部代码就是一切。例如,在我的类中设置变量的函数可以定义为

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.

这似乎符合我在一些地方读到的内容,即在一个功能良好的程序中,断言应该是不可能违反的,而例外应该是针对仍然可能发生的异常和错误情况。因为理论上我要验证所有输入,所以我的断言不应该被触发。如果是,我的程序就错了。

其他回答

我承认使用了断言,但没有考虑适当的错误报告。然而,这并不意味着它们在正确使用时是非常有用的。

They are especially useful for if you want to follow the "Crash Early" principle. For example suppose you're implementing a reference counting mechanism. At certain locations in your code you know that the refcount should be zero or one. And also suppose that if the refcount is wrong the program won't crash immediately but during the next message loop at which point it will be difficult to find out why things went wrong. An assert would have been helpful in detecting the error closer to its origin.

与其说是坏事,不如说是适得其反。永久性错误检查和调试是分开的。Assert让人们认为所有的调试都应该是永久性的,使用过多会导致大量的可读性问题。在需要时,永久错误处理应该比这种方法更好,由于assert会导致自己的错误,因此这是一种非常值得怀疑的实践。

我很喜欢用断言。我发现当我第一次构建应用程序(可能是一个新的领域)时,它非常有用。我没有做非常花哨的错误检查(我认为是不成熟的优化),而是快速编码,并添加了大量断言。在我了解了更多事情的工作原理后,我重新编写并删除了一些断言并更改它们以更好地处理错误。

因为断言,我花更少的时间编码/调试程序。

我还注意到,断言可以帮助我想到许多可能破坏程序的东西。

不,去做和断言都不是坏事。但两者都可能被误用。

Assert用于完整性检查。如果它们不正确,就会杀死程序。不是为了验证或替换错误处理。

我从不使用assert(),示例通常是这样的:

int* ptr = new int[10];
assert(ptr);

这很糟糕,我从来没有这样做过,如果我的游戏分配了一堆怪物怎么办?为什么我要让游戏崩溃,相反,你应该优雅地处理错误,所以可以这样做:

CMonster* ptrMonsters = new CMonster[10];
if(ptrMonsters == NULL) // or u could just write if(!ptrMonsters)
{
    // we failed allocating monsters. log the error e.g. "Failed spawning 10 monsters".
}
else
{
    // initialize monsters.
}