The Go
language creators write:
Go doesn't provide assertions. (...) Programmers use them as a crutch to avoid thinking about proper error handling and reporting.
What is your opinion about this?
The Go
language creators write:
Go doesn't provide assertions. (...) Programmers use them as a crutch to avoid thinking about proper error handling and reporting.
What is your opinion about this?
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.
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.
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.
They should be used for detecting bugs in the program. Not bad user input.
If used correctly, they are not evil.
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".
At some point I'll have a look at Go.
I think that point has been postponed by the realisation that Go is explicitly designed for use only by weak programmers. It appears the designers wish to avoid a feature which improves the productivity of good programmers, and the quality of their code. They do this in order to encourage bad programmers to find novel ways to screw up their error-handling. Forgive me if I don't fall over myself in my rush to join them.
If they had said, "in our experience, they are used exclusively as a crutch" etc, then I might have had some sympathy with their reasoning, although I think I'd question their research. If they had said "we don't want to mess about with a preprocessor", then I'd have looked with interest at their proffered alternative for ensuring that dead code is kept out of production.
With enough optimisation you can almost write your own assert function. For instance (in pseudo-C or -C++ but avoiding the preprocessor):
const int global_assert_switch = 0;
void assert(bool condition) {
if ((global_assert_switch == 1) && (!condition)) {
print_stack_trace();
abort();
}
}
Then you need a guarantee that the compiler will eliminate the entire function call as dead code if global_assert_switch is 0. You also need a guarantee that the compiler will eliminate the evaluation of the argument expression if it has no side effects. Then finally you need to only assert conditions that have no side effects (which of course should be the case already, but there's usually a big difference between "the programmer knows this has no side effects" and "the compiler knows this has no side effects").
Seems a lot of hassle for me, just to kick away a few crutches from some other programmers somewhere else. Of course if Go has weave points, then all may be forgiven, since they provide an alternate way to optimise production code by removing debugging aids.
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).
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.
As an additional information, go provides a built-in function panic
and panicln
. This can be used in place of assert
. E.g.
if x < 0 {
panicln('x is less than 0');
}
panic
and panicln
will print the stack trace, so in some way it has the purpose of assert
.
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.
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.
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/archives/000500.html
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?
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.
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 :)
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.
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'");
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?
Even if you consider it a limitation in the Go language that there is no assert, it would be an easy job to build your own assert go library :)