In languages such as
Eiffel, assertions form part of the design process; other languages, such as
C and
Java, use them only to check assumptions at
runtime. In both cases, they can be checked for validity at runtime, but can usually also be suppressed.
Assertions in design by contract Assertions can function as a form of documentation: they can describe the state the code expects to find before it runs (its
preconditions), and the state the code expects to result in when it is finished running (
postconditions); they can also specify
invariants of a
class.
Eiffel integrates such assertions into the language and automatically extracts them to document the class. This forms an important part of the method of
design by contract. This approach is also useful in languages that do not explicitly support it: the advantage of using assertion statements rather than assertions in
comments is that the program can check the assertions every time it runs; if the assertion no longer holds, an error can be reported. This prevents the code from getting out of sync with the assertions. For example, the following demonstrates design by contract in C++ (using
C++26 contracts). int f(const int x) pre(x != 1) // a precondition assertion post(r : r == x && r != 2) // a postcondition assertion; r names the result object of f { contract_assert(x != 3); // an assertion statement return x; }
Assertions for run-time checking An assertion may be used to verify that an assumption made by the programmer during the implementation of the program remains valid when the program is executed. For example, consider the following
Java code: int total = countNumberOfUsers(); if (total % 2 == 0) { // total is even } else { // total is odd and non-negative assert total % 2 == 1; } In
Java, % is the
remainder operator (
modulo), and in Java, if its first operand is negative, the result can also be negative (unlike the modulo used in mathematics). Here, the programmer has assumed that total is non-negative, so that the remainder of a division by 2 will always be 0 or 1. The assertion makes this assumption explicit: if countNumberOfUsers does return a negative value, the program may have a bug. A major advantage of this technique is that when an error does occur, it is detected immediately and directly, rather than later through often obscure effects. Since an assertion failure usually reports the code location, one can often pinpoint the error without further debugging. Assertions are also sometimes placed at points where the execution is not supposed to reach. For example, assertions could be placed at the default clause of the switch statement in languages such as
C,
C++, and
Java. Any case that the programmer does not handle intentionally will raise an error, and the program will abort rather than silently continuing in an erroneous state. In
D such an assertion is added automatically when a switch statement doesn't contain a default clause. In
Java, assertions have been a part of the language since version 1.4. Assertion failures result in raising an AssertionError when the program is run with the appropriate flags, without which the assert statements are ignored. In
C, they are added on by the standard header defining assert(
assertion) as a macro that signals an error in the case of failure, usually terminating the program. In
C++, both and headers provide the assert macro. The danger of assertions is that they may cause side effects either by changing memory data or by changing thread timing. Assertions should be implemented carefully, so they cause no side effects on program code. Assertion constructs in a language allow for easy
test-driven development (TDD) without the use of a third-party library. In
C#, there is no assertion macro or keyword, but instead classes System.Diagnostics.Debug and System.Diagnostics.Trace which provide Assert() methods. In
Rust, there is an assert!() macro.
Assertions during the development cycle During the
development cycle, the programmer will typically run the program with assertions enabled. When an assertion failure occurs, the programmer is immediately notified of the problem. Many assertion implementations will also halt the program's execution: this is useful, since if the program continued to run after an assertion violation occurred, it might corrupt its state and make the cause of the problem more difficult to locate. Using the information provided by the assertion failure (such as the location of the failure and perhaps a
stack trace, or even the full program state if the environment supports
core dumps or if the program is running in a
debugger), the programmer can usually fix the problem. Thus, assertions provide a very powerful tool in debugging.
Assertions in production environment When a program is deployed to
production, assertions are typically turned off to avoid any overhead or side effects they may have. In some cases, assertions are completely absent from deployed code, such as in C/C++ assertions via macros. In other cases, such as Java, assertions are present in the deployed code and can be turned on in the field for debugging. Assertions may also be used to promise the compiler that a given edge condition is not actually reachable, thereby permitting certain
optimizations that would not otherwise be possible. In this case, disabling the assertions could actually reduce performance.
Static assertions Assertions that are checked at compile time are called static assertions. Static assertions are particularly useful in compile-time
template metaprogramming, but can also be used in low-level languages like C by introducing illegal code if (and only if) the assertion fails.
C11 and
C++11 support static assertions directly through static_assert. In earlier C versions, a static assertion can be implemented, for example, like this: • define SASSERT(pred) switch(0){case 0:case pred:;} SASSERT( BOOLEAN CONDITION ); If the (BOOLEAN CONDITION) part evaluates to false, then the above code will not compile because the compiler will not allow two
case labels with the same constant. The Boolean expression must be a compile-time constant value, for example (
sizeof(int)==4) would be a valid expression in that context. This construct does not work at file scope (i.e., not inside a function), and so it must be wrapped inside a function. Another popular way of implementing assertions in C is: static char const static_assertion[ (BOOLEAN CONDITION) ? 1 : -1 ] = {'!'}; If the (BOOLEAN CONDITION) part evaluates to false, then the above code will not compile because arrays may not have a negative length. If, in fact, the compiler allows a negative length, then the initialization byte (the '!' part) should cause even such over-lenient compilers to complain. The Boolean expression must be a compile-time constant value, for example (sizeof(int) == 4) would be a valid expression in that context. Both of these methods require a method of constructing unique names. Modern compilers support a __COUNTER__ preprocessor define that facilitates the construction of unique names, by returning monotonically increasing numbers for each compilation unit.
D provides static assertions through the use of static assert. ==Disabling assertions==