契約プログラミング

Dの契約プログラミングはコードベースが期待通りに振る舞うことを確実にする サニティ・チェックを反映させることによりコードの質を向上させることができる 言語構成体群を含みます。契約はデバッグモードでのみ利用でき、 リリースモードでは実行されません。したがってユーザの入力を 検証するのに使われてはいけません。

assert

Dの最も単純な契約プログラミングの形はassert(...)式で、 これは一定の条件を満たすかをチェックし、 そうでない場合はAssertionErrorでプログラムを中断します。

assert(sqrt(4) == 2);
// 任意のカスタムアサーションエラーメッセージ
assert(sqrt(16) == 4, "sqrt is broken!");

関数の契約

inoutで入力パラメータと関数の返値の契約を形式化することができます。

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);
}

inブロックの中のコンテンツは関数の本文中に表現することもできますが、 この方法のほうが意図が明確です。outブロックの中で関数の返値は out(result)でキャプチャしそこで検証されます。

不変条件のチェック

invariant()structclass型の特殊なメンバ関数で、 オブジェクトのライフタイム全体におけるその状態のサニティ・チェックを行うものです:

  • それはコンストラクタが実行された後とデストラクタが呼ばれる前に呼ばれます
  • それはメンバ関数に入る前に呼ばれます
  • invariant()はメンバ関数を抜けたあとに呼ばれます。

ユーザ入力を検証する

すべての契約はリリースビルドで削除されるため、ユーザ入力は契約を使って検証されるべきではありません。 加えて(訳注:例外ではなく)致命的なErrorsを投げるため、assertnothrow関数で使うことができます。 実行時のassertの類似物はstd.exception.enforceで、 これはキャッチできるExceptionsを投げます。

掘り下げる

rdmd playground.d