Почему `$ @` ненадежен? - PullRequest
       14

Почему `$ @` ненадежен?

12 голосов
/ 10 сентября 2010

Кажется, я помню, что доверять значению $@ после eval небезопасно. Что-то о том, что обработчик сигнала может установить $@ до того, как вы его увидите или что-то в этом роде. Я также слишком устал и ленив, чтобы разыскать настоящую причину. Итак, почему нельзя доверять $@?

Ответы [ 3 ]

17 голосов
/ 10 сентября 2010

В perldoc Try::Tiny подробно обсуждается проблема с $@:

Существует ряд проблем с eval.

Clobbering $ @

Когда вы запускаете блок eval, и он успешно выполняется, $ @ будет очищено, что может привести к сбою ошибки, которая в данный момент обнаруживается.

Это вызывает действиена расстоянии, сбрасывая предыдущие ошибки, возможно, ваш вызывающий еще не обработал.

$ @ должен быть правильно локализован перед вызовом eval, чтобы избежать этой проблемы.

В частности, $ @ заточенв начале eval, что также делает невозможным захват предыдущей ошибки перед вашей смертью (например, при создании объектов исключений со стеками ошибок).

По этой причине попытка фактически установит $ @ к предыдущемузначение (до локализации) в начале блока eval.

Локализация $ @ молча маскирует ошибки

Внутри блока eval матрица ведет себя какнапример:

sub die {
        $@ = $_[0];
        return_undef_from_eval();
}

Это означает, что если вы были вежливы и локализованы $ @, вы не можете умереть в этой области, иначе ваша ошибка будет отклонена (вместо этого будет напечатано «Что-то не так»).

Обходной путь очень уродлив:

my $error = do {
        local $@;
        eval { ... };
        $@;
};

...
die $error;

$ @ может быть неверным значением

Этот код неверен:

if ( $@ ) {
        ...
}

потому что из-за предыдущих предупреждений он мог быть не установлен.

$ @ также может быть перегруженным объектом ошибки, который оценивается как ложный, но это все равно вызывает проблемы.

Классический режим отказаis:

sub Object::DESTROY {
        eval { ... }
}

eval {
        my $obj = Object->new;

        die "foo";
};

if ( $@ ) {

}

В этом случае, поскольку Object :: DESTROY не локализует $ @, но все еще использует eval, он установит $ @ в "".

Деструктор вызывается, когдастек разматывается, после того как die устанавливает $ @ в "foo at Foo.pm line 42 \ n", поэтому к моменту вычисления ($ @) он был очищен с помощью eval в деструкторе.

Обходной путь для этого еще более уродлив, чем предыдущие.Несмотря на то, что мы не можем сохранить значение $ @ из кода, который не локализуется, мы можем по крайней мере быть уверены, что eval был прерван из-за ошибки:

my $failed = not eval {
        ...

        return 1;
};

Это потому, что evalпойманный кубик всегда будет возвращать ложное значение.

13 голосов
/ 10 сентября 2010

Try :: Tiny docs имеют довольно хороший список недостатков eval / $@. Я думаю, что вы, возможно, ссылаетесь на раздел Localization $ @, который молча маскирует ошибки .

7 голосов
/ 10 сентября 2010

$@ имеет те же проблемы, что и каждая глобальная переменная: когда что-то еще устанавливает ее, она сбрасывается во всей программе. Любой eval может установить $@. Даже если вы не видите eval поблизости, вы не знаете, кто еще может вызвать его (подпрограммы, связанные переменные и т. Д.).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...