Contract programming

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

Function contracts

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 checking

invariant() is a special member function of struct and class types that allows sanity checking an object's state during its whole lifetime:

  • It's called after the constructor has run and before the destructor is called.
  • It's called before entering a member function
  • invariant() is called after exiting a member function.

Validating user input

As all contracts will be removed in the release build, user input should not be checked using contracts. Moreover asserts 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.

In-depth

rdmd playground.d