例外

このガイドではユーザExceptionについてのみ取り扱います。 システムErrorは通常致命的で、キャッチすべきではありません

例外をキャッチする

例外の一般的な例は、無効である可能性のあるユーザの入力を確認することです。 例外が投げられると、スタックは最初にマッチする例外ハンドラが見つかるまで巻き戻されます。

try
{
    readText("dummyFile");
}
catch (FileException e)
{
    // ...
}

複数のcatchブロックや、エラーが起きたかどうかに関係なく実行されるfinallyブロックを持つことができます。 例外はthrowによって投げられます。

try
{
    throw new StringException("You shall not pass.");
}
catch (FileException e)
{
    // ...
}
catch (StringException e)
{
    // ...
}
finally
{
    // ...
}

スコープガードが通常try-finallyパターンの良い手法であることを覚えておいてください。

カスタム例外

Exceptionから簡単に継承ができ、カスタム例外を作ることができます:

class UserNotFoundException : Exception
{
    this(string msg, string file = __FILE__, size_t line = __LINE__) {
        super(msg, file, line);
    }
}
throw new UserNotFoundException("D-Man is on vacation");

nothrowで安全な世界へ

Dコンパイラは関数が壊滅的副作用を引き起こさないことを保証できます。 そのような関数はnothrowキーワードで注釈することができます。 nothrow関数内でDコンパイラは例外を投げることを静的に禁止します。

bool lessThan(int a, int b) nothrow
{
    writeln("unsafe world"); // 出力は例外を投げる可能性があり、従ってこれは禁止です。
    return a < b;
}

コンパイラはテンプレート化されたコードの属性を推論できることを覚えておいてください。

std.exception

ユーザー入力に対するassertによる 契約プログラミング を避けることは重要です。 リリースモードでのコンパイル時に契約は除去されてしまいます。 利便性のためにstd.exceptionenforceを提供しており、 これはassertのように使えますが、AssertErrorのかわりにExceptionを投げます。

import std.exception : enforce;
float magic = 1_000_000_000;
enforce(magic + 42 - magic == 42, "Floating-point math is fun");

// カスタム例外を投げる
enforce!StringException('a' != 'A', "Case-sensitive algorithm");

しかしstd.exceptionにあるのははこれだけではありません。例えばエラーが 致命的でないかもしれない時、それをcollectすることをオプトインできます:

import std.exception : collectException;
auto e = collectException(aDangerousOperation());
if (e)
    writeln("The dangerous operation failed with ", e);

テスト内で例外が投げられるかどうかテストするためには、assertThrownを使います。

掘り下げる

rdmd playground.d