Чем плох eval?!
В perl, как всем известно, имеется 2 варианта вызова функции eval. Строковый eval для компиляции и выполнения кода на лету и блочный eval.
Нас интересует блочный eval.
Часто встречается такой вот код:
eval {
# some code
die "error";
# some code
};
if ($@) {
print "Error [$@] occured!";
}
Это вполне стандартный код, но есть ряд недостатков связанных и с использованием глобальной переменной $@.
Потенциальная проблема #1
Если кто-то локализировал переменную $@, то мы не сможем перехватить исключение. В этом коде мы не видим ошибки.
sub do_die {
die "Error";
}
eval {
local $@;
do_die();
};
if ($@) {
print "Error [$@] Occured!";
}
Потенциальная проблема #2
Если в деструкторе используется eval, то скорее всего мы не перехватим исключение. Пример:
package Object;
sub new {
my $class = shift;
my $self = {};
bless $self, $class;
}
sub DESTROY {
eval {
# some clean code
}
}
package main;
eval {
my $obj = Object->new();
die "Error";
};
if ($@) {
print "Error [$@] Occured!";
}
Деструктор с eval-ом может быть в ком-то модуле с CPANа и Вы просто не будете знать про это.
Потенциальная проблема #3
$@ может содержать объект, которые в булевом контексте вернет ложь.
Решение
В связи со всем вышесказанным, рекомендую использовать модуль Try::Tiny либо TryCatch. Они позволят Вам избежать описанных проблем в будущем и предоставят удобные и понятные синтаксический конструкции(try{}catch{} finally{}).
Я использую Try::Tiny, он мне нравится своей минималистичностью и отсутствием внешних зависимостей.
Вот пример кода с Try::Tiny. Красиво и надежно :).
# handle errors with a catch handler
try {
die "foo";
}
catch {
warn "caught error: $_"; # not $@
};
# just silence errors
try {
die "foo";
};
Более доходчиво про это все написано в "perldoc Try::Tiny".