Perl 的eval 有两种形式,字符串评估和块评估。 String eval 调用编译器来执行源代码。块 eval 将已编译的代码包围在一个将捕获 die 异常的包装器中。 (字符串 eval 也捕获了die 异常,以及任何编译错误)。
Try::Tiny 仅适用于 eval 的块形式,但以下适用于两种形式。
每次调用eval 都会改变$@ 的值。如果 eval 成功或 eval 捕获的错误,它将是 ''。
这意味着每次调用 eval 时,都会清除之前的所有错误消息。 Try::Tiny 为您本地化了 $@ 变量,因此成功的 eval 不会清除先前失败的 eval 的消息。
另一个陷阱来自使用$@ 作为检查以确定 eval 是否成功。一个常见的模式是:
eval {...};
if ($@) {
# deal with error here
}
这依赖于两个假设,首先,$@ 可能包含的任何错误消息都是真值(通常为真),并且在 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 很重要,因为如果您必须使用字符串 eval,Try::Tiny 将无济于事。