6

I'm aware of the fact that $@ is a global variable, still I can't figure out why I need to localize it before using eval:

For instance:

eval { SOME_FUNC_THAT_MAY_DIE(); };
if ($@) {
  print "An error occured!\n";
}

The only possible thing I can think of is, if some signal handler will call die at the same time I try to read $@, what am I missing here?

cjm
  • 61,471
  • 9
  • 126
  • 175
snoofkin
  • 8,725
  • 14
  • 49
  • 86

3 Answers3

12

The reason to say local $@ before calling eval is to avoid stepping on your caller's $@. It's rude for a subroutine to alter any global variables (unless that's one of the stated purposes of the subroutine). This isn't really an issue with top-level code (not inside any subroutine).

Also, on older Perl's, any eval called during object destruction would clobber the global $@ (if the object was being destroyed because an exception was being thrown from an eval block) unless $@ was localized first. This was fixed in 5.14.0, but many people are still running older Perls.

cjm
  • 61,471
  • 9
  • 126
  • 175
9

The Try::Tiny module documentation gives the rationale (as well as providing an alternative):

When you run an eval block and it succeeds, $@ will be cleared, potentially clobbering an error that is currently being caught. This causes action at a distance, clearing previous errors your caller may have not yet handled. $@ must be properly localized before invoking eval in order to avoid this issue. More specifically, $@ is clobbered at the beginning of the eval, which also makes it impossible to capture the previous error before you die (for instance when making exception objects with error stacks).
JRFerguson
  • 7,426
  • 2
  • 32
  • 36
  • The reason I found this post, was exactly because I didn't understand exactly this paragraph in Try::Tiny's documentation. What does `potentially clobbering an error that is currently being caught` mean? ;-) – Peter V. Mørch Dec 03 '15 at 00:48
0

You don't need to, but if you wrote code like this, localizing $@ would keep the first error as it was. and if you didn't write code like this, the local $@ would have no effect. better would be to handle errors before running any extra code.

eval {
    die "error 1\n";
};
foo();
print "processing $@\n";

sub foo {
    #local $@;
    eval {
        die "error 2\n";
    };
}
Jake
  • 2,106
  • 1
  • 24
  • 23