Ответ 1
Try::Tiny
perldoc имеет окончательное обсуждение проблемы с [email protected]
:
Существует ряд проблем с eval.
Clobbering [email protected]
Когда вы запустите блок eval и он преуспеет, [email protected]будет очищен, что потенциально сбивает ошибку, которая в настоящее время улавливается.
Это вызывает действие на расстоянии, очищая предыдущие ошибки, которые ваш вызывающий абонент еще не обработал.
[email protected]должен быть правильно локализован перед вызовом eval, чтобы избежать этой проблемы.
Более конкретно, [email protected]сбивается в начале eval, что также делает невозможным захват предыдущей ошибки перед тем, как вы умрете (например, при создании объектов исключений со стеками ошибок).
По этой причине try фактически установит [email protected]на свое предыдущее значение (до локализации) в начале блока eval.
Локализация [email protected]молча маскирует ошибки
Внутри блока eval block ведет себя вроде:
sub die { [email protected] = $_[0]; return_undef_from_eval(); }
Это означает, что если вы были вежливым и локализованным [email protected], вы не можете умереть в этой области или ваша ошибка будет отброшена (вместо этого напечатайте "Что-то не так" ).
Обходной путь очень уродлив:
my $error = do { local [email protected]; eval { ... }; [email protected]; }; ... die $error;
[email protected]может не быть истинным значением
Этот код неверен:
if ( [email protected] ) { ... }
потому что из-за предыдущих предостережений он может быть отменен.
[email protected]также может быть перегруженным объектом ошибки, который оценивает значение false, но это все равно вызывает проблемы.
Классический режим отказа:
sub Object::DESTROY { eval { ... } } eval { my $obj = Object->new; die "foo"; }; if ( [email protected] ) { }
В этом случае, поскольку Object:: DESTROY не локализует [email protected], но все еще использует eval, он установит [email protected]на ".
Деструктор вызывается, когда стек разматывается, после того как die устанавливает [email protected]на" foo на Foo.pm line 42\n", поэтому к моменту вычисления ($ @) он был очищен eval в деструктор.
Обходной путь для этого еще более уродливый, чем предыдущие. Несмотря на то, что мы не можем сохранить значение [email protected]из кода, который не локализуется, мы можем по крайней мере убедиться, что eval был прерван из-за ошибки:
my $failed = not eval { ... return 1; };
Это потому, что eval, который поймал die, всегда будет возвращать ложное значение.