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

The ; 1 is to make sure I get a true value no matter what the function returns. If it fails, I think there are cases where the block still might return a true value, but I don't know.

Replies are listed 'Best First'.
Re^4: Net::SSH2 test connection to remote host
by JavaFan (Canon) on Sep 30, 2008 at 12:06 UTC
    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 ... }
      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.