views:

2588

answers:

20
+45  Q: 

Is assert evil?

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?

+2  A: 

Short answer: No, I believe assertions can be useful

Brendan
Really? I mean that was your contribution to this debate? And, three people upvoted...
Evan Carroll
+37  A: 

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.

gahooa
+1. Assert is precisely what the name says - "I hereby assert that this will always be true (if it's not, then we are fscked at this point)". I also find it ironic how many people misuse the word now - e.g. I hear stuff like "if this check fails here, we should assert and quit" all the time.
Pavel Minaev
I beg to differ on `goto`. `assert` offers something useful, something that can't be easily replaced. I've yet to see a problem where a `goto` was actually the *best* solution.
jalf
@jalf: let's see how much time will pass before someone rushes in and tells you about nested loops
shylent
*Usually*, a nicer solution is to factor it out into a separate function, from which you can return directly - rather than `goto`'ing out of the loops. But yes, it is a common argument from the "goto isn't evil just misunderstood" brigade. Personally I'd rather say "goto isn't evil, just useless".
jalf
@jalf - local `goto`s in C functions are about the most elegant way I've seen for simulating RAII. The cleanup code is all in one location, and all other failures `goto` some part of the cleanup chain.
Tom
@shylent: what is the problem about exiting nested loops with goto? do you have a nicer way? @jalf, what if you make use of 10 local attributes? is nicer to pass them all to that function?
fnieto
@jalf - All of the control flow structures present in modern languages are simply structured uses of "goto". When a while loop hits the end delimiter, it goes to the top. When a if condition is false, it goes to the else clause. Who's to say that `do, while, if, else, for, etc...` ALWAYS cover ALL cases?
gahooa
@gahooa: One loop construct and one conditional construct will cover all cases, and the others are there for convenience. That's been shown. There's a lot of room for argument as to what are the most convenient ways to express control flow, but we've known what's actually needed for decades.
David Thornley
@jalf: why do you think a function is always more elegant than a goto? Is a function not a goto essentially? In fact, I'd argue it's a bigger jump since you usually see the target label on the same screen. Not so with helper functions.
Jurily
@Tom: Yes, but C was designed as a "portable assembly language". It's hardly a good example of a high level language. @fnieto: Then you refactor.@gahooa: So? What's the relevance? Gotos are evil in the context of *programming language source code*. They are widely used in machine code and in assembler, of course. But by your logic, we shouldn't use functions or any other abstractions at all because they don't exist on the lowest level. @Jurily: Because until now, I've never seen a case where the goto was more elegant. And no, a function is not "essentially a goto".
jalf
@jalf: you inverted my argument. I said don't TAKE AWAY the lower level constructs just because higher ones exist. I'm a programmer - give me both, so I can be most productive.
gahooa
+10  A: 

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.

arhuaco
+100  A: 

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.
caf
The practice of writing assertions also makes you think more about your code as you write it. You are thinking about what assumptions you are making and you can then write some assertions to verify them.They also act like a poor-man's unit test. If you make certain changes, assertions will let you know if you messed anything up.
Mike Weller
@Mike I agree, and I also think it's a decent way of documenting what was assumed w.r.t. parameters of a function.
Pieter
there is nothing inherently wrong with goto either... If you can write an assert.... you can write an exception for it... why not go directly with exceptions and leave asserts behind
Newtopian
Mike, why not write real unit tests?
Len Holgate
Because a unit test doesn't tell you when an assumption is violated *in your actual program*. It only tells you when it happens in the specific units you test. So a better question is, why not do *both*?
jalf
Jalf, I agree, which is why I use Exceptions for those kinds of violations. I can test with a unit test that expects the exceptions to occur and if somehow the code gets into that state in production then I still get an exception and we can log it and work out what went wrong. I find that assertions get in the way of unit tests (unless you have ones that integrate nicely with your test harness) and often vanish in production code. That's why IMHO unit tests and exceptions are good and assertions are evil.
Len Holgate
@Newtopian: why would you want to throw an exception for a bug in your program? The whole point of assert is that after you see one, something _already_ went wrong, and a method for continuing from that state has either not been thought of or at least has not been implemented. If you have code with asserts in other situations, then yes, it could make sense to throw instead. But it's pretty common to use assert to check something about function params, and easier to debug with a c++ debugger, than to throw an InvalidArgException or something.
Peter Cordes
@Peter : yes, sure, but the "bug" can have many sources, it can be a code error but it can be related to the data. Once you remove all the state checking that asserts provide you loose the possibility to detect some situations where erroneous information can enter your zone of trust and cause havoc, but this time, the "bug" goes undetected and your system can have unpredictable behavior. Only very rarely this redundant checking becomes problematic so performance wise so the optimization of removing this extra code with today`s system is antiquated....
Newtopian
... cont ... besides How is the code "Assert(condition, "some text"); is much more complicated than if(condition) throw("some text");.. because you have to deal with the exception later in your code ? Maybe so, but at least the safety net remains active if, however unlikely, the situation does return in the future. As a final argument, the checking that asserts do can also be done in unit tests, again, with added advantages that the debug code and shipped code does not change and the information gathered from errors can be a lot more expressive with tests that it could with asserts.
Newtopian
Safety net? What safety net? That's the entire point. You use an assert when there *is* no safety net. When you can no longer trust your own application. The only possible safety net is to debug and fix the problem. @Len Holgate: You make a good case for why Asserts aren't *essential*. The combination of exceptions and unit tests can solve many of the same issues. But that does not make asserts **evil**. At worst it makes them unnecessary. By the way, you can't blame asserts for the shortcomings of your unit test framework. A sensible one *should* be able to handle asserts.
jalf
Jalf; IMHO asserts are evil because they're often overused. In the same way that goto can be considered harmful. Both asserts and goto can be used to your advantage if you know what you're doing BUT IMHO the unit testing and exception approach is superior and is less likely to lead to problems. Agree re the unit test framework should be able to handle asserts.
Len Holgate
+16  A: 

They should be used for detecting bugs in the program. Not bad user input.

If used correctly, they are not evil.

Alex
+22  A: 

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".

jalf
+1 for the breakpoints part.
Seth Illgard
+1  A: 

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.

Steve Jessop
+3  A: 

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).

figurassa
+1  A: 

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.

Charles Eli Cheese
assert is good for declaring the pre-conditions at the top of a function, and if clearly written, acts as part of the documentation of the function.
Peter Cordes
+9  A: 

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.

yuku
A: 

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.

Vulcan Eager
In your code sample the assert is used for testing a precondition, which is one of the main purposes of assert.
StackedCrooked
Yes. I'm very undecided about this... assert does it's job... it also allows programmers to forget to do theirs (i.e. "proper error reporting").
Vulcan Eager
Having error checking at every level of your program can be tedious, slow and a waste of disk space. If you have a function like that and it is not meant to be called outside some scope you control using an assert, is, IMO, sufficient if the function assumes that the parameters have been checked before. Contrast this to checking some pointer in five call levels. Why not check it once at the level it's most convenient and use assert() only as a debuggin means?
Makis
In your example, you replace a division-by-zero-exception with an assertion failure. IMO not a big gain. Anyway, I don't like your "proper, correct version" but maybe it's just because your example is so simple.
ammoQ
@Makis: Just using assert here is dangerous. I've had such cases in my code, where I just asserted that a passed pointer is not NULL, and it never happened. But in production code it happened due to bad user input. A third-party function returned NULL instead of throwing an exception if an input file was not found.
+3  A: 

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.

StackedCrooked
+1  A: 

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

Len Holgate
By the same reasoning, knives are evil, since they can hurt badly when misused. `assert` is for impossible situations.
David Thornley
Unfortunately, far too often they're used for things that aren't impossible situations. This is possibly because they're taught and they're easy to throw into code. So, IF you are writing code with proper, production quality error handling, AND you're striving to design your APIs in such a way that the design minimises API error AND you're unit testing and none of these things help in that particular situation THEN your assert might not be evil...
Len Holgate
I almost -1 you but i know that wouldnt be fair. I just plainly want to say i completely disagree and i did read your post.
acidzombie24
acidzombie24 - that's fine; the world would be boring if everyone agreed. I haven't -1'd any posts here that I disagreed with either. Each to their own; I just happen to KNOW that my approach works very well for me and in my situation is significantly superior (in terms of resulting software reliability, ease of production bug tracking, ease of unit testing for 'shouldn't happen' behaviour and general development speed) to the use of assert.
Len Holgate
+3  A: 

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?

Newtopian
In C and C++, `assert` is a macro, based on the preprocessor, and can have information not contained in exceptions (like file name and line number). Aside from that, it's hard to see why not to throw instead.
David Thornley
David, if you really need the file name and line number then you could wrap your 'throw' in a macro that provides them to the exception that's being thrown. Personally I would always use an exception over an assert for all of the reasons given in this answer and I don't think I've ever missed the fact that they don't have line numbers and filenames in them; they're just infinitely more expressive in their own right!
Len Holgate
The whole point of assert is that after you see one, something already went wrong, and a method for continuing from that state has either not been thought of or at least has not been implemented. Maybe memory is corrupted, and exception handlers might well crash. It's pretty common to use assert to check something about function params, and easier to debug with a c++ debugger than to throw an InvalidArgException or something. (unless you have your debugger stopping at all throw events). After catching an exception, you don't have the stack and locals of the throw site.
Peter Cordes
Peter, you hit the nail on the head for why asserts aren't a good idea; "and a method for continuing from that state has either not been thought of or at least has not been implemented". I'd suggest that it should have been. Throwing in an assert rather than putting in place proper error handling is not a good plan. It may well be easier to debug, but what happens when that code path is taken by production code and your handy assert isn't present (though it's arguably just as bad if it IS still present but the debugger and developer isn't...).
Len Holgate
I hate asserts but in some cases they are a necessary evil since it just may be the only way to get the job done. however in platforms where alternatives are present, or better in the case of Go here, when building your own, there is no reason to continue using them. For this it is only natural that they be deprecated and eventually removed altogether, one may call this evolutionary attrition, where mechanisms are replaced by newer more efficient systems.
Newtopian
At this point, in newer platform asserts should only exist as syntactic sweeteners over normal exception mechanics so that programmers that refuse (there are so many reasons for this) to move on be allowed to carry the habit without detrimental effect on the whole work.
Newtopian
+2  A: 

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.

Pavel Radzivilovsky
+1  A: 

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 :)

Adrian
+3  A: 

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.

jtolle
+1 This is the example for the difference between exceptions and assertions I've seen here.
That's not to say that assertions about arguments are evil either. It's very common to have internal routines which are only ever called by your own code. It's entirely reasonable to "assume" that when you call them, you call them correctly with valid inputs. Assertions document the assumption (and enforce it at debug time to alert you when you screw up). Exceptions in those routines would be misleading to other programmers, since you don't actually ever expect them to fire. And wasteful, since the checks are left in production code.
jtolle
See also the accepted answer here: http://stackoverflow.com/questions/1467568/debug-assert-vs-exception-throwing
jtolle
A: 

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'");

acidzombie24
You're arguing this because your language of *choice* has bad syntax and makes one more convenient than the other? And, because you do this "all the time".
Evan Carroll
No, i am arguing that something is making a TOOL harder or not available on purpose because they 'feel' it isnt useful or because people don't know know how to use it.
acidzombie24
A: 

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?

Evan Carroll
If you look at my answer. My use is to differentiate 'exceptions' (asserts) that i want to get rid of used for debugging vs exceptions that i keep. Why would i want to get rid of them? because without them working it would not be complete. Example is if i handle 3 cases and the 4th is todo. I can easily search assert to find them in code and know its incomplete rather then use an exception that might accidentally be caught (by another programmer) or hard to tell if i its an exception or a logic check that i should solve in code.
acidzombie24
In my eyes, this is a poor idea, on the same level as "sealed" classes and for just the same reason. You're assuming the exceptions you want to keep are acceptable for uses of your code you don't yet know of. All exceptions go through the same channels, and if the user doesn't want to catch them he can choose not to. If he does, he should have the ability too. Either way, you're just making assumptions or pushing off your practice with a concept like an assertion.
Evan Carroll
I decided example scenarios are best. Heres a simple one. int func(int i) { if(i>=0) { console.write("The number is positive {0}", i); } else { assert(false);//to lazy to do negatives ATM } return i*2; <-- How would i do this without asserts and is an exception really better? and remember, this code will be implemented before release.
acidzombie24
Sure exceptions are better, lets say I take user input and call `func()` with a negative number. Now, suddenly with your assertions you've pulled the carpet out from under me and not given me a chance to recover, rather than politely telling me what I'm requesting can't be done. There is nothing wrong with accusing the program of being misbehaved and issuing it a citation: the problem is you're blurring the act of enforcing a law, and sentencing the felon.
Evan Carroll
Thats the point, you SHOULDNT say what the user wants to do cant be done. The app should terminate with the error msg and whomever is debugging would remember its something that SHOULD BE DONE but isnt. You DONT want it to be handled. You want a termination and to be reminded you didnt handle that case. and its extremely easy to see what cases are left since all you need to do is a search for assert in code. Handling the error would be wrong since the code should be able to do it once ready for production. Allowing a programmer to catch it and do something else is wrong.
acidzombie24
NOTE to others. The func in my example above should do `console.write("The number is negative {0}", i); ` and not throw an exception. This is why assert is a DEBUG tool and not an error handling tool. Thats why no one should write `draw(){ Assert(image.exist())` because saying it isnt handle or the user did it wrong (exceptions) isnt the same as saying it will be handled but isnt yet.
acidzombie24
re `You DONT want it to be handled.`. You shouldn't code assuming you know the use-cases of your code. It doesn't matter what you want, if someone else wants to handle it they should be allowed to, it is their freedom to do what they wan't and you shouldn't impose yourself with code. You disagree, you want to be the controller of your codes application. That is fine, I just disagree that it is ever a good thing. Your examples say something of your argument, if you try to draw an image that doesn't exist, the whole program should die because it failed your assertion... right...
Evan Carroll
No you misunderstand. I am not saying you shouldnt be able to handle it the way you like. I am saying if the function should be able do something like load an image and it doesnt then an assert should be in place because your not done the code yet and if you accidentally load an image forgetting your not done it you'll notice it instead of running into exception code. This is why the draw code should never check if an image exist with an assert. An assert belongs in an empty loadimage function and an exception belongs in a draw function when no image has been loaded.
acidzombie24
+2  A: 

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 :)

hhafez