Miért nincs try-catch blokk a Go programozási nyelvben?
A go nyelvi környezet szándékosan nem támogatja a try-catch mechanizmust, csak a panic-recover nyelvtani szerkezetet. Ez számos, a try-catch hibakezelési módszerhez szokott fejlesztő elégedetlenségét váltotta ki. Miért nem implementálták a try-catch-et? Mert a try-catch számos problémát rejt magában.
try-catch-finally
A try-catch (try-catch) utasítás a program futása közben (runtime) fellépő hiba- és kivételes helyzetek kezelésére szolgál. A finally utasítás pedig olyan kódrészleteket tartalmaz, amelyeknek feltétlenül végre kell hajtódniuk, függetlenül attól, hogy kivétel történt-e vagy sem.
Hiba kezelés és felelősség
A 80-as és 90-es években a hiba kezelése nagyon egyszerű volt. A hibaüzenetek a következőkből álltak: „Floppy lemez tele”, „Nincs floppy lemez a meghajtóban”, „Nincs írási jogosultság a floppy lemezre”, és a fejlesztők a hiba felmerülésekor a hibát a hiba kezelési pontra dobva (throw) közösen kezelték azokat. Ebben a környezetben a try-catch utasítás hatékonyan működött.
Azonban az idő múlásával a helyzet megváltozott. Az üzleti logika bonyolultabbá vált, megjelentek az adatbázisok és a tranzakciók, számos API-t hívtak meg hálózaton keresztül, ami rengeteg kérési üzenet értelmezését igényelte, sőt, a párhuzamos programozás megjelenésével a főszálon kívüli, más szálakban is kellett hibákat kezelni.
A hibák kezelése túl bonyolulttá vált ahhoz, hogy egy helyen lehessen elvégezni, és már nem lehetett egyetlen helyen felelősséget vállalni minden hibáért. Itt jelentkezik a try-catch komoly problémája.
A try-catch felelősségáthárítása
A try-catch lényegében azt jelenti, hogy a hiba kiváltója a hiba kezelésének (utókezelés) felelősségét valakire áthárítja. Ez a „valaki” lehet a catch utasítás, a szülő metódus, vagy akár a szülő szülőjének a szülőjének… a szülője. Más szavakkal, egyre bonyolultabb hiba kezelési világban a try-catch által választott módszer az „már majd valaki megoldja” elv. Nézzük meg az alábbi kódot.
1try {
2 data = readFile("hello.txt");
3 structuredData = parseData(data);
4 insertDBStatus(structuredData[1]);
5 startHTTPServer(structuredData[2]);
6} catch (Exception e) {
7 e.printStackTrace();
8}
A fenti kód problémája, hogy nem egyértelmű, hogy ki kezeli a printStackTrace-t, és hogy melyik kódrészletben történt a hiba. Sőt, minél több logika kerül a try utasításba, annál rosszabb a helyzet. Paradox módon azonban, minél bonyolultabbá válik a fejlesztés, annál inkább a fejlesztők a felelősségáthárító try-catch utasítás rabjai lesznek, nem foglalkoznak a hiba kezeléssel, felelősségtudatuk gyengül, és végül elfelejtik a hiba- és kivételkezelés lényegét. Hogyan oldotta meg ezt a golang?
panic, recover
A go egyik előnye, hogy számos olyan rendszerrel rendelkezik, amelyek nem engedik, hogy a fejlesztők rossz útra térjenek, hanem jó fejlesztővé váljanak. A panic-recover is ilyen példa.
A panic és a recover első pillantásra nem különbözik a try-catch-től, de abban különbözik, hogy a probléma felmerülésekor nem hárítja át a felelősséget külsőleg. Ha go-ban panic történik, a programnak a panic helyén kell megoldást találnia, nem pedig a hibát kifelé átadni. Ez a fejlesztőre hárítja a hiba kezelésének felelősségét, arra kényszerítve, hogy alaposabban fontolja meg, hol, ki és hogyan kell kezelni a hibát. A felhasználónak szabadságot biztosít, de teret hagy a gondolkodásra.
A panic szó megválasztása is nagyon jó, mert a try-recover-rel ellentétben a panic-recover egyértelműen csak nyilvánvaló hibahelyzetekben használható, ami nyomást gyakorol a fejlesztőre. A fejlesztő így nem fogja túlzásba vinni a recover használatát, csak a szükséges helyeken alkalmazza. Ez nagyban segíti a go-ban a tömör kód írását.