我似乎记得在$@
之后信任eval
的价值是不安全的。关于信号处理程序有可能在你看到它之前设置$@
的东西。我现在也太累了,懒得追查真正的原因。那么,为什么信任$@
?
答案 0 :(得分:17)
Try::Tiny
perldoc对$@
的问题进行了明确的讨论:
eval存在许多问题。
Clobbering $ @
当您运行eval块并且成功时,$ @将被清除,可能会破坏当前捕获的错误。
这会导致远距离操作,清除调用者可能尚未处理的错误。
$ @必须在调用eval之前正确本地化,以避免此问题。
更具体地说,$ @在eval的开头被破坏了,这也使你无法在死之前捕获先前的错误(例如在制作具有错误堆栈的异常对象时)。
因此,尝试实际上将$ @设置为eval块开头的前一个值(在本地化之前)。
本地化$ @静默掩盖错误
在eval块中,die的行为类似于:
sub die { $@ = $_[0]; return_undef_from_eval(); }
这意味着如果你礼貌和本地化的$ @你不能死在那个范围内,或者你的错误将被丢弃(打印出“Something's wrong”)。
解决方法非常难看:
my $error = do { local $@; eval { ... }; $@; }; ... die $error;
$ @可能不是真正的值
此代码错误:
if ( $@ ) { ... }
因为之前的警告可能没有设置。
$ @也可能是一个评估为false的重载错误对象,但无论如何都要求麻烦。
经典的失败模式是:
sub Object::DESTROY { eval { ... } } eval { my $obj = Object->new; die "foo"; }; if ( $@ ) { }
在这种情况下,由于Object :: DESTROY没有本地化$ @但仍然使用eval,它会将$ @设置为“”。
在堆栈展开后调用析构函数,在模具将$ @设置为“Foo.pm第42行的foo”之后,所以当if($ @)被计算时,它已被eval清除了析构函数。
这方面的解决方案甚至比以前的方法更加丑陋。即使我们无法从未本地化的代码中保存$ @的值,我们至少可以确保eval因错误而中止:
my $failed = not eval { ... return 1; };
这是因为捕获骰子的eval将始终返回错误值。
答案 1 :(得分:13)
Try::Tiny docs有一个很好的eval
/ $@
个缺点列表。我想你可能会引用那里的Localizing $@ silently masks errors部分。
答案 2 :(得分:7)
$@
具有与每个全局变量相同的问题:当其他东西设置它时,它会在整个程序中重置。任何eval
都可能设置$@
。即使您没有在附近看到eval
,也不知道还有谁可以调用它(子程序,绑定变量等)。