Should I put the “eval” in the subroutine or the subroutine in the “eval”?

谁都会走 提交于 2019-12-23 02:36:39

问题


Does one of these two ways have to be preferred or is it only a matter of taste?

#!/usr/bin/env perl
use warnings;
use strict;
use DBI;

my $db = 'sqlite_db';

####################   A   ####################

sub get_database_handle {
    my ( $db ) = @_;
    my $dbh;
    eval {
        $dbh = DBI->connect( "DBI:SQLite:$db", '', '', {...} )
        or die DBI->errstr;
    };
    if ( $@ ) {
        print $@;
        return;
    }
    return $dbh;
}

DATABASES: while ( 1 ) {
    # choose a database from a list of databases
    # ...
    my $dbh = get_database_handle( $db );
    next DATABASES if not defined $dbh;
    # ...
    # do something with the database
}

####################   B   ####################

sub get_database_handle {
    my ( $db ) = @_;
    my $dbh = DBI->connect( "DBI:SQLite:$db", '', '', {...} ) 
    or die DBI->errstr;
    return $dbh;
}

DATABASES: while ( 1 ) {
    # choose a database from a list of databases
    # ...
    my $dbh;
    eval { $dbh = get_database_handle( $db ); };
    if ( $@ ) {
        print $@;
        next DATABASES;
    }
    # ...
    # do something with the database
}

回答1:


Why eval at all? What are you going to do when you can't get a database handle?

At the very least, the subroutine should either return a database handle or die, so no eval in there.

If you really have something productive to do when there is a database error, then eval outside of the subroutine. Not necessary around just the subroutine, could be even wider scope, as appropriate for your error handling logic.

But if all you want is terminate the program and print an error, just let the exception bubble up.




回答2:


It really makes no sense to use RaiseError => 1 at all if your code is going to look like that. If you want to keep that code layout, use RaiseError => 0 instead. (You can always turn it on later using $dbh->{RaiseError} = 1;`.)

sub get_database_handle {
    my ( $db ) = @_;
    return DBI->connect("DBI:SQLite:$db", '', '', {
        ...
        RaiseError => 0,
        PrintError => 1,
    });
}

for my $db ( ... ) {
    my $dbh = get_database_handle( $db )
       or next;

    ...
}

That said, I suggest that you continue using RaiseError => 1, but that you change your code layout instead. Specifically, you should widen the scope of the eval.

sub get_database_handle {
    my ( $db ) = @_;
    return DBI->connect("DBI:SQLite:$db", '', '', {
        ...
        RaiseError => 1,
        PrintError => 0,
    });
}

for my $db ( ... ) {
    if (!eval {    
        my $dbh = get_database_handle( $db );

        ...

        1  # No exception
    }) {
        warn("Error processing database $db: $@");
    }
}

This will catch any errors, not just database connection errors.




回答3:


It depends on the preferred way of handling errors in the rest of your project.

  • If you plan to use exceptions, let your function throw them.

  • If you are going to handle errors manually via conditionals, don't throw (eval inside).

I myself prefer exceptions. They are loud (you know something is broken!), plus stack traces via Carp::confess/Carp::longmess and $SIG{__DIE__} are a nice bonus in case of a large codebase.

Here's an (un)success story. A month or two ago we rolled out broken code that silently corrupted data due to unchecked return value from DB-handling function. It was in production for a day. Resurrecting the data was a world of pain.



来源:https://stackoverflow.com/questions/12635495/should-i-put-the-eval-in-the-subroutine-or-the-subroutine-in-the-eval

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!