211

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.

How would these arguments apply to C or C++? What are some pros and cons of using assert()?

user16217248
  • 3,119
  • 19
  • 19
  • 37
Frank
  • 64,140
  • 93
  • 237
  • 324
  • 6
    tangent: Go is an unusually opinionated language. This isn't necessarily a bad thing. However, it does mean you should take its opinions with a bigger grain of salt. It also means that if you disagree, you are going to be gnashing your teeth as you use the language. As evidence of how Go clings to its opinions despite reality, consider that you need to resort to the magic of reflection to determine if two collections are equal. – allyourcode Oct 04 '15 at 20:39
  • @allyourcode If you're referring to [`reflect.DeepEqual`](https://golang.org/pkg/reflect/#DeepEqual), you certainly don't *need* it. It's convenient, but at the cost of performance (unit tests are a good use case). Otherwise, you can implement whatever equality check is appropriate for your "collection" without too much trouble. – Igor Dubinskiy Dec 31 '15 at 09:40
  • 1
    No, that's not what I'm talking about. There is no such thing as slice1 == slice2 without reflection. All other languages have an equivalent to this super basic operation. The only reason Go does not is prejudice. – allyourcode Jan 15 '16 at 20:14
  • You can compare two slices without reflection using a `for` loop in Go (just like C). It *would* be really nice to have generic slice operations, although comparison gets complicated when pointers and structs are involved. – kbolino Jan 15 '18 at 04:08

21 Answers21

329

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
  • 233,326
  • 40
  • 323
  • 462
  • In other words, an assertion failure exhibits that there is a **programming error** (either the code or the assertion condition is wrong). Exception handling is for **exceptional cases** (like IO errors) that are out of control of the programmer. For me, the actual point of the assertions is that **they prevent us to enter an uncertain world**. – Mustafa Özçetin Jun 03 '23 at 09:06
113

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
  • 131,293
  • 12
  • 98
  • 101
  • how to use `goto` wisely? – ar2015 Apr 07 '16 at 06:52
  • 3
    @ar2015 Find one of the absurdly contrived patterns that some people recommend to avoid `goto` for purely religious reasons, then just use `goto` rather than obfuscating what you're doing. In other words: if you can prove you really need `goto`, and the only alternative is to enact a load of pointless scaffolding that ultimately does the same thing but without alterting the Goto Police... then just use `goto`. Of course, a precondition of this is the bit "if you can prove you really need `goto`". Often, people don't. That still doesn't mean it's inherently a Bad Thing. – underscore_d Apr 19 '16 at 15:01
  • 2
    `goto` is used in linux kernel for code cleanup – malat Apr 26 '16 at 13:25
  • The only place so far I found goto to be absolutely superior is to create an abort-retry-ignore logic with user confirmation. It makes the code much easier to read and write in that case (this is just an opinion). The other not-so-superior case is to break outside switch statements to another switch statement in languages like C# that do not allow fallthrough. – Gaspa79 Dec 01 '20 at 12:32
62

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
  • 243,077
  • 51
  • 345
  • 550
41

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.

Mehdi Charife
  • 722
  • 1
  • 7
  • 22
arhuaco
  • 1,718
  • 16
  • 19
31

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.

user253751
  • 57,427
  • 7
  • 48
  • 90
Randy Sugianto 'Yuku'
  • 71,383
  • 57
  • 178
  • 228
30

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

If used correctly, they are not evil.

Alex Budovski
  • 17,947
  • 6
  • 53
  • 58
13

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
  • 7,023
  • 2
  • 28
  • 50
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).

figurassa
  • 2,037
  • 2
  • 13
  • 12
6

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
  • 18,794
  • 5
  • 57
  • 67
5

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
  • 34,653
  • 44
  • 154
  • 278
5

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?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Newtopian
  • 7,543
  • 4
  • 48
  • 71
  • Go does not have exceptions. The usual reason to use an assert rather than an exception is because you want it elided in deployment for performance reasons. Asserts are not archaic. I'm sorry to sound harsh, but this answer is not remotely helpful to the original question, nor correct. – Nir Friedman Apr 13 '15 at 19:35
4

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.

David Stone
  • 26,872
  • 14
  • 68
  • 84
4

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:

Even people that advocate for assert think they should only be used in development and not in production:

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.

Hjulle
  • 2,471
  • 1
  • 22
  • 34
hhafez
  • 38,949
  • 39
  • 113
  • 143
2

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

Len Holgate
  • 21,282
  • 4
  • 45
  • 92
1

Short answer: No, I believe assertions can be useful

Brendan Lesniak
  • 2,271
  • 4
  • 24
  • 48
1

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.

Agnel Kurian
  • 57,975
  • 43
  • 146
  • 217
1

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

akuhn
  • 27,477
  • 2
  • 76
  • 91
  • 1
    Not too bad :-) Exceptions or not, assertions or not, Go fans are still talking about how short are the codes. – Zippo Jan 03 '12 at 18:50
1

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
  • 78,363
  • 46
  • 261
  • 468
  • 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. –  Jan 28 '10 at 21:17
  • 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 Jan 29 '10 at 00:55
  • 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. –  Jan 29 '10 at 10:52
  • 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 Jan 29 '10 at 16:30
  • 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. –  Jan 29 '10 at 19:51
  • 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. –  Jan 29 '10 at 19:58
  • 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 Jan 29 '10 at 21:04
  • 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. –  Jan 29 '10 at 21:15
  • The difference is "exceptional" vs. "impossible". See here: http://stackoverflow.com/questions/1467568/debug-assert-vs-exception-throwing/1468385#1468385 – jtolle Dec 21 '10 at 17:11
1

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.
}
Bob
  • 1
  • 1
0

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.

  • 5
    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 Dec 09 '09 at 00:54
0

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

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131