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 only available in debug mode and won't be run in release mode. Therefore they shouldn't be used to validate user input.

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 to formalize contracts for input parameters and return values of functions.

long square_root(long x)
in {
    assert(x >= 0);
} out (result) {
    assert((result * result) <= x
        && (result+1) * (result+1) > x);
} body {
    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 be 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