http://www.perlmonks.org?node_id=278900


in reply to Re2: Learning how to use the Error module by example
in thread Learning how to use the Error module by example

I wouldn't just go spreading FUD abour Error.pm. It has very serious problems, and the only reason I didn't describe them is that I thought they were well-known. The try/catch syntax is implemented using sub ref prototypes. Take a look at this code:
use Error qw( :try ); sub important_function { try { something_dangerous(); } catch Error::Simple with { return 0; }; return 1; }
What value do you think this sub will return if it catches an error? It will always return 1, because the return inside of the catch block just returns from the implicit subroutine that the catch block creates. Nasty.

Possibly worse, the nested closure problem can cause memory leaks in a long-running process. The problem is partially described by Matts in this presentation. Here's an example of a sub that will leak memory every time you call it:

use Error qw( :try ); sub leaky_function { my $foo = 7; try { # do something here try { $foo++; } catch Error::Simple with { # whatever }; } catch Error::Simple with { # whatever }; }
I hope I've got this code right; it's been a while since I used it.

UPDATE: This leak seems to have been fixed in recent versions of Perl. That's a nice thing to see!

We used Error in the system we built at eToys, and after having both of the problems I descibe here I have decided never to use the try/catch syntax again. It's dangerous and the problems are hard to see.

Replies are listed 'Best First'.
Re4: Learning how to use the Error module by example
by dragonchild (Archbishop) on Jul 29, 2003 at 17:51 UTC
    Excellent! ++! Now, that explains what's going on. It's a little worrying that these examples are not in the Error documentation. A few thoughts:

    The first example, to me, is an example of how not to use try-catch. To me, you shouldn't be doing a return 0 within a catch. You should be rethrowing some error after doing what you needed to do. The only return statement should be as a result of success. Regardless of memory leaks, that's just poor practice.

    The second method ... the closure concern is a bit harsher for me. Matts does provide a solution for it, but I don't like it as it removes the major benefit imho, which is the syntactic sugar. I'll have to think about it and see whether it has a good workaround.

    ------
    We are the carpenters and bricklayers of the Information Age.

    Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

    Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

      The first example, to me, is an example of how not to use try-catch. To me, you shouldn't be doing a return 0 within a catch. You should be rethrowing some error after doing what you needed to do. The only return statement should be as a result of success. Regardless of memory leaks, that's just poor practice.

      I think that's a rather broad statement. Many people (myself included) think that multiple-returns can make code a lot clearer in some circumstances. For example, while you could rewrite this with a single return

      sub find { my ($self, $search_item) = @_; eval { # skip expensive search if item not stored anywhere return unless $search_item->stored; # otherwise do expensive search $self->first; while (my $item = $self->next) { return 1 if $item == $search_item; }; return; }; if ($@) { ... handle exceptions here ... }; };

      I would argue that it would be harder to grok than the version above.

      For me the main problem with Error is the fact that wrapping a bit of code in a catch block shouldn't change its semantics. This can lead to really odd behaviour when fiddling with exception based code.

        My point is that if you're wrapping in a try-block, you should be throwing exceptions, even something like Error::OK or the like. In fact, I wouldn't even call them exceptions, really. They're more like signals within the same process. Just because we use them 99.999% of the time to indicate an abnormal situation doesn't mean that this is what they should be limited to ...

        ------
        We are the carpenters and bricklayers of the Information Age.

        Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

        Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

      My return example is contrived, but there are situations where one might want to return from within a try block and it's counterintuitive to have to avoid it. This problem came up in the course of normal use. We were not looking for trouble.