What are the common pitfalls associated with Perl's eval
, whic开发者_如何转开发h might make you choose to use a module such as Try::Tiny
?
Perl's eval
comes in two flavors, string eval and block eval. String eval invokes the compiler to execute source code. Block eval surrounds already compiled code in a wrapper that will catch the die
exception. (string eval catches the die
exception also, as well as any compilation errors).
Try::Tiny only applies to the block form of eval, but the following applies to both forms.
Every time you call eval
it will change the value of $@
. It will either be ''
if the eval succeeded or the error caught by the eval.
This means that any time you call an eval, you will clear any previous error messages. Try::Tiny
localizes the $@
variable for you, so that a successful eval will not clear the message of a previous failed eval.
The other pitfall comes from using $@
as the check to determine if the eval succeeded. A common pattern is:
eval {...};
if ($@) {
# deal with error here
}
This relies on two assumptions, first that any error message $@
could contain is a true value (usually true), and that there is no code between the eval block and the if statement.
Visually of course the latter is true, but if the eval block created an object, and that object went out of scope after the eval failed, then the object's DESTROY
method will be called before the if
statement. If DESTROY
happens to call eval without localizing $@
and it succeeds, then by the time your if
statement is run, the $@
variable will be cleared.
The solution to these problems is:
my $return = do {
local $@;
my $ret;
eval {$ret = this_could_fail(); 1} or die "eval failed: $@";
$ret
};
breaking that apart line by line, the local $@
creates a new $@
for the do
block which prevents clobbering previous values. my $ret
will be the return value of the evaluated code. In the eval block, $ret
is assigned to, and then the block returns 1
. That way, no matter what, if the eval succeeds it will return true, and if it fails it will return false. It is up to you what to do in the case of failure. The code above just dies, but you could easily use the return value of the eval block to decide to run other code.
Since the above incantation is a bit tedious, it becomes error prone. Using a module like Try::Tiny
insulates you from those potential errors, at the cost of a few more function calls per eval. It is important to know how to use eval properly, because Try::Tiny
is not going to help you if you have to use a string eval.
The issues are explained in the Try::Tiny documentation. Briefly, they are:
- Clobbering
$@
- Localizing
$@
silently masks errors $@
might not be a true value
In addition to the answers above, I would add...
- eval is affected by the global
$SIG{__DIE__}
handler causing action at a distance. - It is easy for a novice to confuse
eval BLOCK
andeval STRING
, since they appear to do the same thing, but one is a security hole.
Try::Tiny has its own pitfalls, the biggest being that while it looks like a block it is actually a subroutine call. That means this:
eval {
...blah blah...
return $foo;
};
and this:
try {
...blah blah...
return $foo;
};
do not do the same thing. These are laid out in the CAVEATS section of the Try::Tiny docs. That said, I'd recommend it over eval
.
Using eval on X11 function might still failed to keep alive.
The code is like
eval {
@win_arrays = GetWindowsFromPid($pid);
};
The script will be exited from
X Error of failed request: ...
精彩评论