Perl's eval
выпускается в двух вариантах: струнный и блочный. String eval вызывает компилятор для выполнения исходного кода. Блок eval окружает уже скомпилированный код в оболочке, которая перехватит исключение die
. (строка eval также перехватывает исключение die
и все ошибки компиляции).
Try :: Tiny применяется только к блочной форме eval, но следующее относится к обеим формам.
Каждый раз, когда вы звоните eval
, это изменит значение $@
. Это будет либо ''
, если eval завершился успешно, либо ошибка, обнаруженная eval.
Это означает, что каждый раз, когда вы вызываете eval, вы удаляете все предыдущие сообщения об ошибках. Try::Tiny
локализует переменную $@
для вас, так что успешное вычисление не удалит сообщение предыдущего неудачного вычисления.
Другая ловушка возникает из-за использования $@
в качестве проверки, чтобы определить, удалось ли eval. Общий шаблон:
eval {...};
if ($@) {
# deal with error here
}
Это основывается на двух предположениях: во-первых, любое сообщение об ошибке $@
может содержать истинное значение (обычно true) и что между блоком eval и оператором if нет кода.
Визуально, конечно, последнее верно, но если блок eval создал объект, и этот объект вышел из области видимости после сбоя eval, то метод DESTROY
объекта будет вызван перед оператором if
. Если DESTROY
случайно вызовет eval без локализации $@
, и это произойдет, то к тому времени, когда будет запущен оператор if
, переменная $@
будет очищена.
Решение этих проблем:
my $return = do {
local $@;
my $ret;
eval {$ret = this_could_fail(); 1} or die "eval failed: $@";
$ret
};
разрывая эту строку за строкой, local $@
создает новый $@
для блока do
, который предотвращает слипание предыдущих значений. my $ret
будет возвращаемым значением оцененного кода. В блоке eval присваивается $ret
, а затем блок возвращает 1
. Таким образом, несмотря ни на что, если eval преуспеет, он вернет true, а если потерпит неудачу, вернет false Вам решать, что делать в случае неудачи. Приведенный выше код просто умирает, но вы можете легко использовать возвращаемое значение блока eval, чтобы принять решение запустить другой код.
Поскольку вышеприведенное заклинание немного утомительно, оно подвержено ошибкам. Использование такого модуля, как Try::Tiny
, защищает вас от этих потенциальных ошибок, за счет еще нескольких вызовов функций за один eval. Важно знать, как правильно использовать eval, потому что Try::Tiny
не поможет вам, если вам придется использовать строку eval.