A Go programozási nyelv

Hibakezelés

Az error típus

A nyelv rendelkezik egy előre definiált error interface típussal:

type error interface { Error() string }

Ez a szokásos, hibajelzésre használatos interface olyan módon, hogy a nil érték jelzi, ha nincs hiba. Hiba esetén pedig az érték (legalább) a hibaüzenet kiírására alkalmas Error függvényt biztosítja.

További konvenció, hogy az Error string tartalmazza a csomag nevét, ahol a hiba keletkezett, pl.: image: processing error.

Visszatérési értékkel

A Go nyelvben a legtöbb hibát a függvények visszatérési értékével jelzik. Ezt megkönnyíti, hogy a függvényeknek több visszatérési értéke is lehet, így az eredményt és az esetleges hibajelzést egyszerre vissza lehet adni.

Ebben az esetben is az error típus a szokásos hibajelzésre. A hívó oldalon a hiba kezelése például így nézhet ki:

if err := api.Func(); err != nil { // hiba kezelése fmt.Println("err: " + err.String()) }

Panic és recover

Az olyan súlyos hibák kezelésére, amikor a program nem folytatható, kínálja a nyelv a panic és recover függvényeket.

A panic függvény

A panic beépített függvény futásidejű hibát generál. Egyetlen, tetszőleges típusú paramétert vár (ez a gyakorlatban többnyire egy string, esetleg egy error típusú érték), ez lesz a hibaüzenet.

Ez a függvény használható akkor is, ha valamilyen lehetetlennek tűnő esemény történt, például véget ért egy (elvileg) végtelen ciklus. Ezt a fordító is támogatja: ha a függvény végén panic áll, a return elhagyható.

Nemcsak a felhasználó hívhatja meg a panic függvényt, ez implicit hívódik meg például tömb túlindexelésekor, típushibáknál vagy egyéb futásidejű hibáknál is.

A panic híváskor azonnal megszakítja a függvény futását, és elkezdi kiüríteni az aktuális gorutin vermét, esetleg végrehajtva a defer kulcsszóval ellátott függvényeket (ld. alább). Ha a hibát a program sehol sem kezeli, a verem kiürül, a program futása leáll, és kiírja a panic paraméterét a képernyőre.

A panic gyakori használatát a nyelv készítői nem tartják szerencsés megoldásnak. Ha egy felmerülő hiba kezelésére, elkerülésére és a program további helyes futásának biztosítására lehetőség van helyben, akkor felesleges kockáztatni a teljes program leállását (vagy a könyvtárunk felhasználóit nagy mennyiségű hibakezelő kód írására kényszeríteni).

A recover függvény

A recover szintén beépített függvény, paramétere nincs. Hívásakor megállítja a panic terjedését, hogy a hibát kezelni lehessen. Visszatérési értéke a hibaérték: interface{} típusú. ha ez az érték nil, akkor nem történt hiba.

Ahhoz, hogy a panic végrehajtsa a recover-t tartalmazó kódot, a defer kulcsszót kell használni. A defer utáni függvény- vagy metódushívás nem azonnal hajtódik végre, hanem közvetlenül azelőtt, hogy az őt tartalmazó függvény visszatérne. Ez akkor is igaz, ha a panic kényszeríti visszatérésre a függvényt, így ez az egyetlen módja annak, hogy a panic-ot okozó hibát kezeljük.

func server(workChan <-chan *Work) { for work := range workChan { go safelyDo(work) // dolgozó gorutinok indítása } } func safelyDo(work *Work) { defer func() { // a hibakezelő függvények általában így kezdődnek if err := recover(); err != nil { log.Println("work failed:", err) } }() // ez a függvény csak akkor hívódik meg, amikor a do végzett, vagy hibát generált do(work) }

A fenti példában a server függvény több dolgozót indít el párhuzamosan. Ha bármelyik dolgozóban hiba lép fel, hibaüzenetet ír ki, és a dolgozó leáll anélkül, hogy a teljes program kilépne.

Természetesen a defer függvényben lehetőségünk van a hibát továbbdobni vagy másik hibát kiváltani a panic explicit hívásával. Illetve ha egy ilyen kód egy függvényhívásában újabb panic történik, akkor az "új" hiba a korábbi hívási láncon terjed tovább.

Érdekes tulajdonsága a defer függvényeknek, hogy mivel a visszatérés előtt mindig lefutnak (nem csak panic esetén), ezért a nevesített visszatérési értékeket módosíthatjuk egy ilyen függvényből. Ez hasznos lehet hibakezelésnél is (pl. egy részlegesen elkészült visszatérési értéket alapértékre állíthatunk), de általánosabban is használható.