in reply to Re^3: Net::SSH2 test connection to remote host
in thread Net::SSH2 test connection to remote host

But as it, it's an example of cargo-cult. Sure, $res is true if and only if the eval doesn't fail, but you're not using its result. Instead, you rely on $@ to determine failure. But the entire '; 1' idiom is there because relying on $@ to determine failure can trigger both false positives and false negatives. The correct idiom is:
my $res = eval {...statements...; 1}; unless ($res) { ... eval failed ... }

Replies are listed 'Best First'.
Re^5: Net::SSH2 test connection to remote host
by salva (Canon) on Sep 30, 2008 at 15:45 UTC
    relying on $@ to determine failure can trigger both false positives and false negatives

    Can you explain how?

    The usual way to handle errors from functions that die to signal them is:

    eval { do_whatever() }; if ($@) { # handle error ... }
    or if you want to use the value returned by do_whatever():
    my $res = eval { do_whatever() }; if ($@) { # handle error ... } # use $res here;
    I believe that the 1 usage comes from...
    eval { do_whatever(); 1 } or print "failed!\n";
      The problem is that eval { } makes a scope for the statements to be executed. On scope exit, code may be executed (for instance, DESTROY methods). That code can clear $@, or set it - it may even call eval itself.

      That's the reason you cannot trust $@ to be set if, and only if, the eval failed.

      Here's an example.

      #!/usr/bin/perl use 5.010; use strict; use warnings; use Test::More tests => 3; eval {my $x = bless [], 'one'}; is $@, ""; eval {my $x = bless [], 'two'}; is $@, ""; eval {my $x = bless [], 'three'; die "foo\n";}; is $@, "foo\n"; __END__ 1..3 ok 1 ok 2 ok 3
      Now we change the code by adding some DESTROY methods - and note that this is the only change in the code.
      #!/usr/bin/perl use 5.010; use strict; use warnings; use Test::More tests => 3; eval {my $x = bless [], 'one'}; is $@, ""; eval {my $x = bless [], 'two'}; is $@, ""; eval {my $x = bless [], 'three'; die "foo\n";}; is $@, "foo\n"; sub one::DESTROY {1;} sub two::DESTROY {$@ = "foo";} sub three::DESTROY {$@ = "";} __END__ 1..3 ok 1 not ok 2 # Failed test at /tmp/test line 11. # got: 'foo' # expected: '' not ok 3 # Failed test at /tmp/test line 12. # got: '' # expected: 'foo # ' # Looks like you failed 2 tests of 3.
      As you can see, after the second eval $@ is set where you don't expect it to be (the eval succeeded), and while the third eval died, $@ is false.

      And note that while I did direct assignment to $@ in the DESTROY methods, I could also have used evals in the DESTROY methods to create false positive or negatives.

        In my opinion, DESTROY methods should not have side effects like changing $@ (or $? or $!), and if they do, it is a bug!

        And I don't see that programming defensively against this kind of bugs is the right thing to do. It just complicates the code unnecessarily. The only reason I see to do that is when the DESTROY method is outside your control (i.e. in a package from CPAN).

        But if it's your own code, just...

        sub DESTROY { local $@; ... }