© StonePictures/Shutterstock.com
Kolumne: Die Golumne

Netter Versuch


Wer kennt es nicht? Man gibt sich Mühe, entwirft, programmiert sorgfältig, testet sowohl manuell als auch automatisch, hat also ein gefühlt perfektes Programm. Und doch kommt es immer wieder zur Laufzeit zu Fehlern. Es sind Dateien beim Öffnen nicht vorhanden, sie haben ein falsches Format, sind wegen fehlender Rechte nicht les- oder schreibbar oder können wegen eines gefüllten Dateisystems nicht geschrieben werden. Gleiches gilt auch für das Netz. Adressen sind nicht zu erreichen, Latenzen zu groß, Zugriff wird nicht gestattet und Verbindungen brechen ab. Datenbanken oder Verzeichnisdienste liefern nicht die gesuchten Daten. Und wenn, dann enthalten sie vielleicht ungültige Werte. Die Liste der möglichen Bedrohungen ist unendlich.

Damit ist das Leben eines Programms ein wilder Dschungel. Es muss natürlich zusehen, einige dieser Fehler selbst zu vermeiden. So muss eine Division by Zero nicht unbedingt sein. Doch wie bereits angedeutet kann der eigene Code nicht immer in Watte gepackt werden, insbesondere bei den erwähnten externen Störungen. Selbst wenn man eine zwar sehr funktionale Bibliothek entwickelt und diese auch besonders sorgfältig dokumentiert hat, ist ihre fehlerhafte Nutzung dennoch immer möglich. Man muss sich also auch um eine Fehlerbehandlung kümmern.

In den guten alten C-Zeiten war es der Return-Code: Unterschiedlich negative Integer-Werte konnten als unterschiedliche Fehler interpretiert werden. Doch das wurde in vielen Sprachen durch ein Exception Handling abgelöst. Funktionen beziehungsweise Methoden können hier bei internen Fehlern ihre eigenen Exceptions werfen sowie auch die Exceptions genutzter Funktionen weiterreichen. Sie können aber auch Anweisungen in einem try-catch-Block einrahmen, um geworfene Ausnahmen intern aufzufangen und als eigene Exception wieder zu werfen. Derartige Ausnahmen kann man sich als spezielle, individuelle Fehlertypen mit eigenen Attributen für mehr Details vorstellen. Die wichtige Idee hinter diesem Mechanismus ist es, den fachlichen Block syntaktisch von der Fehlerbehandlung zu trennen. Verständlich, dass das heute der verbreitetste Weg ist, mit Fehlern umzugehen.

Der Weg in Go

Dann betrat Go die Bühne und mit sich führte es 1A-Exceptions. Oder doch nicht? Stattdessen verfügte es über den Typ error, eigentlich nicht mehr als ein Interface mit der Methode Error() string. Das Package errors und auch eine Funktion in fmt helfen, sie zu erzeugen. Doch können auch eigene Typen implementiert werden, die dieses Interface erfüllen und gleichzeitig mehr Informationen als nur einen Text mit sich führen können [1].

Das allein hilft noch nicht. Funktionen mit einem potenziellen Fehler können diesen als individuellen Rückgabewert oder als letzten in einer Reihe von Werten zurückgeben, ähnlich wie in C. Bei den Vätern von Go verwundert das nicht. Ist dieser Fehlerwert nun nil, dann war die Ausführung erfolgreich. Andernfalls drückt der Fehlerwert das interne Problem aus und der Aufruf kann hierauf reagieren. Das kann direkt geschehen, oder das Problem kann an den Aufrufer der eigenen Funktion weitergegeben werden, idealerweise mit ergänzenden Informationen. So gibt es heute diverse Bibliotheken, die das Annotieren von Fehlern mit eigenen Informationen unterstützen. Auch die Bemühungen in den golang.org/x Packages gehen in diese Richtung.

Mit diesem Verfahren rückt die Behandlun...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Gewinner-Teams

Wir bieten Lizenz-Lösungen für Teams jeder Größe: Finden Sie heraus, welche Lösung am besten zu Ihnen passt.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang