What are the common pitfalls associated with Perl\'s eval, which 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.