$@はグローバル変数

あまり意識されていませんが、$@はグローバル変数です。気をつけないとおかしなことになります。以下のコードではdie()で例外を発生させているので「Error is Dummy error」と表示されるように見えますが、表示されません。

    package Hoge;
    sub new { bless {}, shift }
    sub cleanup {
        # 色々処理
    }

    sub DESTROY {
        my $self = shift;
        eval { $self->cleanup };
    }

    package main;
    eval {
        my $hoge = Hoge->new();
        die "Dummy error";
    };
    if ($@) {
        print "Error is $@\n";
    } else {
        print "Everything OK!\n";
    }

evaldie()によって終了し、スコープが切り替わる段階でDESTROYが呼ばれます。その中でeval{}をもう一度呼んでいますが、ここではエラーがなかったため$@が空に設定されるのです。よって、エラーの値を出力するころにはすでに$@は空で、エラーを検知できません。

このようなグローバル変数を変更する可能性のあるコードを書く場合はlocalで修飾すると良いでしょう。

    sub DESTROY {
        my $self = shift;
        local $@;
        eval { $self->cleanup };
    }

なお、全てのeval{}でこの処理を加える必要はありませんが、該当する箇所にエラーがあってもわざとそれを無視する今回のような場合には明示的にlocalをつけておいたほうが安全です。

次はid:hidekさん