スコープ

前の章で見たように、常に実行しなければならない式はfinallyブロックで記述し、エラー条件が発生した場合に実行しなければならない式はcatchブロックで記述する。

これらのブロックの使用について、以下の点が指摘できる。

scope文は、catchおよびfinallyスコープと似た機能を持っているが、多くの点で優れている。finallyと同様に、3つの異なるscope文は、スコープを離れるときに式を実行するものだ。

これらの文は例外と密接に関連しているが、try-catchブロックを使用せずに使用することができる。

例として、上記の関数をscope(failure)文を使って記述しよう。

void foo(ref int r) {
    int addend = 42;

    r += addend;
    scope(failure) r -= addend;

    mayThrow();
}
D

上記のscope(failure)文は、例外によって関数のスコープが終了した場合に、r -= addend式が確実に実行されるようにする。scope(failure)の利点は、別の式を元に戻す式がその式に近い場所に記述できることだ。

scope文はブロックとして指定することもできる。

scope(exit) {
    // ... 式 ...
}
D

3つの文すべてをテストする別の関数を以下に示す。

void test() {
    scope(exit) writeln("when exiting 1");

    scope(success) {
        writeln("if successful 1");
        writeln("if successful 2");
    }

    scope(failure) writeln("if thrown 1");
    scope(exit) writeln("when exiting 2");
    scope(failure) writeln("if thrown 2");

    throwsHalfTheTime();
}
D

例外がスローされなかった場合、関数の出力にはscope(exit)およびscope(success)式のみが含まれる。

when exiting 2
if successful 1
if successful 2
when exiting 1

例外がスローされた場合、出力にはscope(exit)およびscope(failure)式が含まれる。

if thrown 2
when exiting 2
if thrown 1
when exiting 1
object.Exception@...: エラーメッセージ

出力からわかるように、scope文のブロックは逆の順序で実行される。これは、後のコードが前の変数に依存している可能性があるためだ。scope文を逆の順序で実行すると、前の式の副作用を一貫した順序で元に戻すことができる。