Contract programming in D includes a set of language constructs that allow increasing the code quality by implementing sanity checks that make sure that the code base behaves as intended. Contracts are compiled and executed when the software is built for testing or debugging. In release builds (enabled by the -release switch for DMD) they are completely omitted by the compiler, therefore they shouldn't be used to validate user input or as an alternative to using exceptions.
assert
The simplest form of contract programming in D is
the assert(...)
expression that checks that a certain
condition is met - and aborts the program with
an AssertionError
otherwise.
assert(sqrt(4) == 2);
// optional custom assertion error message
assert(sqrt(16) == 4, "sqrt is broken!");
in
and out
allow contracts to be formalized for input
parameters and the return values of functions.
long square_root(long x)
in {
assert(x >= 0);
} out (result) {
assert((result * result) <= x
&& (result+1) * (result+1) > x);
} do {
return cast(long)std.math.sqrt(cast(real)x);
}
The content in the in
block could also be expressed
within the function's body but the intent is much clearer
this way. In the out
block the function's return
value can be captured with out(result)
and
verified accordingly.
invariant()
is a special member function of struct
and class
types that allows sanity checking an object's
state during its whole lifetime:
invariant()
is called after exiting a member
function.
As all contracts will be removed in the release build, user input should not
be checked using contracts. Moreover assert
s can still be used in
nothrow
functions, because they throw fatal Errors
.
The runtime analog to assert
is std.exception.enforce
,
which will throw catchable Exceptions
.