可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
The Go
language creators write:
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.
What is your opinion about this?
回答1:
No, there's nothing wrong with assert
as long as you use it as intended.
That is, it's supposed to be for catching cases that "can't happen", during debugging, as opposed to normal error handling.
- Assert: A failure in the program's logic itself.
- Error Handling: An erroneous input or system state not due to a bug in the program.
回答2:
No, neither goto
nor assert
are evil. But both can be misused.
Assert is for sanity checks. Things that should kill the program if they are not correct. Not for validation or as a replacement for error handling.
回答3:
By that logic, breakpoints are evil too.
Asserts should be used as a debugging aid, and nothing else. "Evil" is when you try using them instead of error handling.
Asserts are there to help you, the programmer, detect and fix problems that must not exist and verify that your assumptions stay true.
They have nothing to do with error handling, but unfortunately, some programmers abuse them as such, and then declare them "evil".
回答4:
I like to use assert a lot. I find it very useful when I am building applications for the first time (perhaps for a new domain). Instead of doing very fancy error checking (that I would consider premature optimization) I code fast and I add a lot of asserts. After I know more about how things work I do a rewrite and remove some of the asserts and change them for better error handling.
Because of asserts I spend a lot of less time coding/debugging programs.
I've also noticed that the asserts help me think of many things that could break my programs.
回答5:
As an additional information, go provides a built-in function panic
. This can be used in place of assert
. E.g.
if x < 0 {
panic("x is less than 0");
}
panic
will print the stack trace, so in some way it has the purpose of assert
.
回答6:
They should be used for detecting bugs in the program. Not bad user input.
If used correctly, they are not evil.
回答7:
This comes up a lot, and I think one problem that makes defenses of assertions confusing is that they are often based on argument checking. So consider this different example of when you might use an assertion:
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
You use an exception for the input because you expect you'll get bad input sometimes. You assert that the list is sorted to help you find a bug in your algorithm, which by definition you don't expect. The assertion is in the debug build only, so even though the check is expensive, you don't mind doing it on every single invocation of the routine.
You still have to unit-test your production code, but that's a different, and complementary, way of making sure your code is correct. Unit tests make sure your routine lives up to its interface, while assertions are a finer-grained way to make sure your implementation is doing exactly what you expect it to.
回答8:
Assertions are not evil but they can be easily misused. I do agree with the statement that "assertions are often used as a crutch to avoid thinking about proper error handling and reporting". I have seen this quite often.
Personally, I do like to use assertions because they document assumptions that I might have made whilst writing my code. If these assumptions are broken while maintaining the code, the problem can be detected during test. However, I do make the point of stripping out every assert from my code when doing a production build (i.e., using #ifdefs). By stripping out the assertions in the production build, I eliminate the risk of anyone misusing them as a crutch.
There is also another problem with assertions. Assertions are only checked at run-time. But it is often the case that the check you would like to perform could have been performed at compile-time. It is preferable to detect an issue at compile time. For C++ programmers, boost provides BOOST_STATIC_ASSERT which allows you to do this. For C programmers, this article ( link text ) describes a technique that can be used to perform assertions at compile time.
In summary, the rule of thumb I follow is: Do not use assertions in a production build and, if possible, only use assertions for things that cannot be verified at compile-time (i.e., must be checked at run-time).
回答9:
I admit having used asserts while not considering proper error reporting. However, that doesn't take away that they are very helpful when used correctly.
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.
回答10:
I prefer avoiding code that does different things in debug and release.
Breaking in the debugger on a condition and having all file/line info is useful though, also the exact expression and the exact value.
Having an assert that would "evaluate the condition only in debug" may be a performance optimization, and as such, useful only in 0.0001% of programs - where people know what they are doing. In all other cases this is harmful, as the expression may actually change program's state:
assert(2 == ShroedingersCat.GetNumEars());
would make the program do different things in debug and release.
We have developed a set of assert macros which would throw an exception, and do it in both debug and release version. For instance, THROW_UNLESS_EQ(a, 20);
would throw an exception with what() message having both file, line and the actual values of a, and so on. Only a macro would have the power for this. The debugger may be configured to break at 'throw' of the specific exception type.
回答11:
I dislike asserts intensely. I would not go as far as saying they are evil though.
Basically an assert will do the same thing as an unchecked exception would, the only exception is that the assert (normally) should not be kept for the final product.
If you build a safety net for yourself while debugging and building the system why would you deny this safety net for your customer, or your support help desk, or anyone that will get to use the software that you are currently building. Use exceptions exclusively for both asserts and exceptional situations. By creating an appropriate exception hierarchy you will be able to discern very quickly one from the other. Except this time the assert remains in place and can provide valuable information in case of failure that would otherwise be lost.
So I fully understand the creators of Go by removing asserts altogether and forcing programmers to use exceptions to handle the situation. There is a simple explanation for this, exception are just a better mechanism for the job why stick with the archaic asserts?
回答12:
Short answer: No, I believe assertions can be useful
回答13:
I've recently started adding some asserts to my code, and this is how I've been doing it:
I mentally divide my code into boundary code and internal code. Boundary code is code that handles user input, reads files, and gets data from the network. In this code, I request input in a loop that only exits when input is valid (in the case of interactive user input), or throw exceptions in the case of unrecoverable file / network corrupt data.
Internal code is everything else. For instance, a function that sets a variable in my class might be defined as
void Class::f (int value) {
assert (value < end);
member = value;
}
and a function that gets input from a network might read as such:
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
.
This seems to fit into what I'm reading in a few places, that assertions should be impossible to violate in a well-functioning program, while exceptions should be for exceptional and erroneous cases that are still possible. Because in theory I'm validating all input, it should not be possible for my assertion to be triggered. If it is, my program is wrong.
回答14:
assert
is very useful and can save you a lot of backtracking when unexpected errors occur by halting the program at the very first signs of trouble.
On the other hand, it is very easy to abuse assert
.
int quotient(int a, int b){
assert(b != 0);
return a / b;
}
The proper, correct version would be something like:
bool quotient(int a, int b, int &result){
if(b == 0)
return false;
result = a / b;
return true;
}
So... in the long run... in the big picture... I must agree that assert
can be abused. I do it all the time.
回答15:
assert
is being abused for error handling because it is less typing.
So as language designers, they should rather see that proper error handling can be done with even lesser typing. Excluding assert because your exception mechanism is verbose is not the solution. Oh wait, Go doesn't have exceptions either. Too bad :)
回答16:
I felt like kicking the author in the head when I saw that.
I use asserts all the time in code and eventually replace them all when I write more code. I use them when I haven't written the logic required and want to be alerted when I run into the code instead of writing an exception which will be deleted as the project gets closer to completion.
Exceptions also blend in with production code more easily which I dislike. An assert is easier to notice than throw new Exception("Some generic msg or 'pretend i am an assert'");
回答17:
My problem with these answers defending assert is no one clearly specifies what makes it different from a regular fatal error, and why an assert can't be a subset of an exception. Now, with this said, what if the exception is never caught? Does that make it an assertion by nomenclature? And, why would you ever want to impose a restriction in the language that an exception can be raised that /nothing/ can handle?
回答18:
Yes, asserts are evil.
Often they get used in places where proper error handling should be used. Get used to writing proper production quality error handling from the start!
Usually they get in the way of writing unit tests (unless you write a custom assert that interacts with your test harness). This is often because they are used where proper error handling should be used.
Mostly they get compiled out of release builds which means that none of their "testing" is available when you're running the code that you actually release; given that in multi-threaded situations the worst problems often only show up in release code this can be bad.
Sometimes they're a crutch for otherwise broken designs; i.e. the design of the code allows a user to call it in a way that it shouldn't be called and the assert "prevents" this. Fix the design!
I wrote about this more on my blog back in 2005 here: http://www.lenholgate.com/blog/2005/09/assert-is-evil.html
回答19:
If the asserts you're talking about mean that the program vomits and then exists, assertions can be very bad. This is not to say that they are always the wrong thing to use, they are a construct that is very easily misused. They also have many better alternatives. Things like that are good candidates for being called evil.
For example, a 3rd party module (or any module really) should almost never exit the calling program. This doesn't give the calling programmer any control over what risk the program should take at that moment. In many cases, data is so important that even saving corrupted data is better than losing that data. Asserts can force you to lose data.
Some alternatives to asserts:
- Using a debugger,
- Console/database/other logging
- Exceptions
- Other types of error handling
Some references:
- http://ftp.gnu.org/old-gnu/Manuals/nana-1.14/html_node/nana_3.html
- http://www.lenholgate.com/blog/2005/09/assert-is-evil.html
- Go doesn't provide assertions and has good reasons why: http://golang.org/doc/faq#assertions
- http://c2.com/cgi/wiki?DoNotUseAssertions
Even people that advocate for assert think they should only be used in development and not in production:
- http://codebetter.com/gregyoung/2007/12/12/asserts-are-not-evil/
- http://www.codeproject.com/Articles/6404/Assert-is-your-friend
- http://parabellumgames.wordpress.com/using-asserts-for-debugging/
This person says that asserts should be used when the module has potentially corrupted data that persists after an exception is thrown: http://www.advogato.org/article/949.html . This is certainly a reasonable point, however, an external module should never prescribe how important corrupted data is to the calling program (by exiting "for" them). The proper way to handle this is by throwing an exception that makes it clear that the program may now be in an inconsistent state. And since good programs mostly consist of modules (with a little glue code in the main executable), asserts are almost always the wrong thing to do.
回答20:
Not so much evil as generally counterproductive. There's a separation between permanent error checking and debugging. Assert makes people think all debugging should be permanent and causes massive readability problems when used much. Permanent error handling ought to be better than that where needed, and since assert causes its own errors it's a pretty questionable practice.
回答21:
i never use assert(), examples usually show something like this:
int* ptr = new int[10];
assert(ptr);
This is bad, i never do this, what if my game is allocating a bunch of monsters? why should i crash the game, instead you should handle the errors gracefully, so do something like:
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.
}