Blog

Auf dem Weg zu Perl 5.36 - try/catch/finally

12.05.2022 // Renée Bäcker

Es ist nicht schön, wenn man eine Anwendung hat, die vielleicht mittendrin einfach aufhört zu laufen. Vielleicht ist die Anwendung in einen Fehler gelaufen und vielleicht gibt es keine ordentliche Fehlermeldung. Woran hat es gelegen? An welcher Stelle ist der Fehler aufgetreten?

In Perl nutzt man häufig eval {} , um Fehler abzufangen und eine ordentliche Fehlerbehandlung zu machen. Das hat aber auch Schwächen, so steckt der Fehler in der Spezialvariablen $@ und je nach Situation könnte diese Variable vor der Fehlerbehandlung wieder geleert werden.

Auf CPAN gibt es mehrere Module, die das try/catch-Konstrukt aus anderen Programmiersprachen als Perl-Modul umsetzen. Seit Perl 5.34 gibt es dieses Werkzeug direkt mit dem Perl-Kern und mit Perl 5.36 noch ein weitere Verbesserung. Das ist allerdings noch als experimentell gekennzeichnet.

Schauen wir uns den Standard-Fall mal an:

eval {
    die "Help!";
};

if ( $@ ) {
    warn "An error occured: $@";
}

In Zeile 2 wird ein die ausgeführt, was zu einem Programmabbruch führen würde. Dank des Block-eval wird dieser Abbruch aber abgefangen und es kann eine ordentliche Fehlerbehandlung durchgeführt werden.

In das try/catch überführt, sieht das so aus:

use feature 'try';
no warnings 'experimental::try';
 
try {
    die 'This died';
}
catch ( $error ) {
    warn "The code inside 'try' died with error: '$error'";
}

Der try-Block sieht genauso aus wie beim eval. Allerdings schließt sich hier direkt ein catch-Block an, dem der Fehler aus dem try-Block übergeben wird. Dieser Fehler landet in $error.

Wird bei dem die ein Fehlerobjekt übergeben, landet genau dieses Objekt auch in $error:

use feature 'try';
no warnings 'experimental::try';
 
package ErrorObject { use Moo; has message => ( is => 'ro' ) }
 
try {
    die ErrorObject->new( message => 'An error occured' );
}
catch ( $error ) {
    warn sprintf "The code inside 'try' died with error: '%s'",
        $error->message;
}

In Perl 5.36 kommt die Möglichkeit hinzu, einen finally-Block zu definieren. Dieser wird auf jeden Fall ausgeführt – egal, ob ein Fehler aufgetreten ist oder nicht. Außerdem ist es egal, ob in dem catch-Block ein exit aufgerufen wird oder nicht.

Dieser Block kann daher gut für abschließende Schritte verwendet werden, die auf jeden Fall ausgeführt werden sollen:

use feature 'try';
no warnings 'experimental::try';
 
package ErrorObject { use Moo; has message => ( is => 'ro' ) }
 
try {
    die ErrorObject->new( message => 'An error occured' );
}
catch ( $error ) {
    warn sprintf "The code inside 'try' died with error: '%s'",
        $error->message;
    exit;
}
finally {
    print "clean up...";
}





Permalink: