Miért nincs Try-Catch a Go-ban?
A Go szándékosan nem támogatja a try-catch szerkezetet, kizárólag a panic-recover mechanizmust. Ez sok fejlesztő nemtetszését váltja ki, akik hozzászoktak a try-catch alapú hibakezeléshez. Miért nem építették be akkor a try-catch-et? Azért, mert a try-catch számos problémát rejt magában.
try-catch-finally
A try-catch szerkezet a program futása során (runtime) felmerülő hibák és kivételek kezelésére szolgáló nyelvi elem. A finally blokk pedig olyan kódot tartalmaz, amely kivétel felmerülésétől függetlenül minden esetben végrehajtódik.
Hibakezelés és felelősség
Az 1980-as és 1990-es években a hibakezelés rendkívül egyszerű volt. A hibaüzenetek kizárólag a következőket jelentették: 'Floppy lemez megtelt', 'Nincs floppy lemez a meghajtóban', 'Nincs írási jogosultság a floppy lemezre'. Ebben az időszakban a fejlesztők a hibákat a hibakezelési pontig továbbították (throw), és ott egységesen kezelték. Ilyen körülmények között a try-catch szerkezet hatékonyan működött.
Az idő múlásával azonban a helyzet megváltozott. Az üzleti logika komplexebbé vált, megjelentek az adatbázisok és a tranzakciók, számtalan API hívást kellett értelmezni a hálózaton keresztül, és a konkurens programozás megjelenésével a hibákat már nem a fő szálon, hanem más szálakon kellett kezelni.
A hibák olyannyira komplexszé váltak, hogy már nem kezelhetők egyetlen helyen, és senki sem vállalhatja a teljes felelősséget minden hibáért. Itt merül fel egy súlyos probléma a try-catch mechanizmussal kapcsolatban.
A try-catch felelősségáthárítása
A try-catch, egy szóval, egy olyan módszer, amellyel a hibát okozó entitás a hibakezelés (utófeldolgozás) felelősségét valaki másra hárítja. Ez a valaki lehet a catch blokk, a szülő metódus, vagy akár a szülő szülőjének szülőjének szülője. Más szóval, egy olyan világban, ahol a hibakezelés egyre gyakoribbá és komplexebbé válik, a try-catch által választott megközelítés az, hogy "valaki majd megteszi". 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 az, hogy nem világos, ki kezeli a printStackTrace-et, és azt sem lehet tudni, melyik kódrészletben történt a hiba. Minél több logika kerül a try utasításba, annál borzasztóbbá válik a probléma. Azonban paradox módon, minél komplexebbé vált a fejlesztés, annál inkább függővé váltak a fejlesztők a felelősséget áthárító try-catch szerkezettől, nem gondolkodtak a hibakezelésen, a felelősségérzetük is elhalványult, és végül elfelejtették a hibák és kivételek kezelésének lényegét. Hogyan oldotta meg ezt a problémát a Golang?
panic, recover
A Go egyik előnye, hogy számos olyan rendszerrel rendelkezik, amely a fejlesztőket jó irányba tereli, ahelyett, hogy rossz útra vinné őket. A panic-recover mechanizmus is ennek példája. A panic és a recover első pillantásra nem tűnik sokban különbözőnek a try-catch-től, azonban abban különbözik, hogy probléma esetén nem hárítja át a felelősséget. Amikor Go-ban panic történik, ahelyett, hogy az értéket továbbítaná, a panic helyén kell megoldani a problémát. Ez a megközelítés felelősséget ró a fejlesztőre a hibakezeléssel kapcsolatban, arra ösztönözve őket, hogy alaposabban gondolják át, hol, ki és hogyan kezelje az adott hibát. Ezáltal autonómiát biztosít a felhasználóknak, ugyanakkor teret enged a gondolkodásnak.
Emellett a "panic" szó kiválasztása is rendkívül találó, mivel a try-recover-rel ellentétben a panic-recover már önmagában is nyomást gyakorol a fejlesztőre, hogy csak nyilvánvaló hibahelyzetekben használja, és ne kivételes esetekben. Természetesen a fejlesztő nem él vissza a recover használatával, és csak ott alkalmazza, ahol szükséges. Ez nagyban hozzájárul a Go tömör kódjának megírásához.