Контрактне програмування D включає у себе набір мовних конструкцій, які дозволяють підвищити якість коду шляхом впровадження перевірок програми на відсутність очевидних помилок, що дає змогу переконатися, що код поводить себе, як передбачалося. Контракти доступні тільки у режимах відлагодження чи тестування та не будуть працювати у режимі релізу. Саме тому вони не повинні використовуватися для перевірки введених користувачем даних або як альтернативу обробки винятків.
assert
Найпростіша форма контрактного програмування у D - це вираз assert(...)
,
який перевіряє, чи певна умова виконана, а також перериває програму за
допомогою AssertionError
, якщо умова не вірна.
assert(sqrt(4) == 2);
// опціонально можна вказати власну помилку для користувача
assert(sqrt(16) == 4, "sqrt не працює!");
in
та out
дозволяють оформити контракти для перевірки вхідних
параметрів і результату функцій.
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);
}
Зміст блоку in
також можна перенести у тіло функції, але втрачається
очевидність. У блоці out
повернення значення функції можна перехопити
за допомогою out(result)
і перевірити коректність результату.
invariant()
- це спеціальна функція struct
та class
, що дозволяє
контролювати стан об'єкта напротязі всього часу його життя:
invariant()
викликається після завершення функції-члена.
Оскільки усі контракти будуть видалені у релізній збірці, дані, введені
користувачем, не повинні перевірятися за допомогою контрактів. При цьому,
assert
-и можуть бути використані у nothrow
-функціях, тому що
вони породжують фатальні помилки (Error
).
Аналогом часу виконання для assert
-ів є std.exception.enforce
,
який буде видавати виключення (Exception
), які дозволяється перехоплювати.