Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error handling with next in Try::Tiny catch block

The following code (a simplified example, I'm actually iterating over a list of objects and trying to trap an exception) performs error handling by going to the next item in the for list. It works, but gives a warning about using the loop control statement within the catch subroutine:

use strict;
use warnings;
use Try::Tiny;
use 5.010;

NUM: for my $num (1 .. 10) { 
  try { 
    if ($num == 7) { 
      die 'ugly number'; 
    } 
  } catch { 
    chomp;
    say qq/got "$_"/; 
    next NUM; 
  }; 
  say qq/number $num/; 
}

Outputs:

number 1
number 2
number 3
number 4
number 5
number 6
got "ugly number at testtry.pl line 9."
Exiting subroutine via next at testtry.pl line 14.
Exiting subroutine via next at testtry.pl line 14.
number 8
number 9
number 10

I can see two ways to work around it -- shut up the warning in the scope of this usage with a scoped no warnings block, or copy the error message off to a temporary variable and check it/next outside of the catch block. The former may have issues I'm overlooking, and the second spreads out the error handling a bit. Which is preferred, or is there another way I'm overlooking?

like image 480
Oesor Avatar asked May 21 '13 13:05

Oesor


2 Answers

Inside the catch block, put a no warnings 'exiting'. This will disable the warning. The strict and warnings pragmas are just there to help you, feel free to disable them lexically when they get in your way.

The perldiag page lists builtin warning and error categories. You can see all messages that will be silenced by disabling this category and decide if it is worth it.

Edit:

You can exploit that a successful try returns undef, but on error you get the value of the catch block. This allows us to do:

NUM: for my $num (1 .. 10) { 
  try {
    die 'ugly number' if $num == 7;
  } catch { 
    chomp;
    say qq/got "$_"/; 
    return 1;       # return some true value
  } and next NUM;   # go to next iteration here, outside the try/catch
  say qq/number $num/; 
}

However, I find the no warnings solution to be more elegant, and far more obvious.

like image 71
amon Avatar answered Oct 26 '22 06:10

amon


The right solution for this case -- and probably for the more general case you're trying to solve -- is to put stuff that should only happen if there are no errors into the try block rather than relying on explicit loop control. That looks like this:

for my $num (1 .. 10) { 
  try { 
    if ($num == 7) { 
      die 'ugly number'; 
    } 
    say qq/number $num/; 
  } catch { 
    chomp;
    say qq/got "$_"/;
  }; 
}

last is the loop control statement that actually needs a bit of extra care, since it has to do more than simply skip the loop body. But consider

try {
  for my $num (1 .. 10) { 
    try {
      die 'ugly number' if $num == 7;
      die 'early exit' if $num == 9;
      say qq/number $num/; 
    } catch {
      die $_ if /^early exit/;
      chomp;
      say qq/got "$_"/;
    }; 
  }
};
like image 45
darch Avatar answered Oct 26 '22 06:10

darch