Proč Go zvolil explicitní návratové hodnoty chyb a panic-recover namísto vyhazování výjimek pomocí try-catch

Go záměrně nepodporuje try-catch a nabízí pouze konstrukci panic-recover. To vyvolává nelibost mnoha vývojářů, kteří jsou zvyklí na zpracování chyb pomocí try-catch. Proč tedy Go try-catch nezahrnuje? Důvodem je, že try-catch má několik problémů.
try-catch-finally
Konstrukce try-catch (try-catch) slouží ke zpracování chybových a výjimečných situací, které mohou nastat během běhu programu (za běhu). Blok finally pak obsahuje kód, který se musí provést bez ohledu na to, zda k výjimce došlo.
Zpracování chyb a odpovědnost
V osmdesátých a devadesátých letech bylo zpracování chyb velmi jednoduché. Chybové zprávy se omezovaly na „disketa je plná“, „v mechanice není disketa“ a „nemáte oprávnění k zápisu na disketu“. Vývojáři tehdy při výskytu chyby předávali chybu pomocí throw až na místo, kde se zpracovávala, a zpracování tak bylo centralizované. Za těchto podmínek konstrukce try-catch fungovala účinně.
Postupem času se však situace změnila. Obchodní logika se zkomplikovala, objevily se databáze a transakce, přes síť se volalo mnoho API a bylo nutné interpretovat velké množství požadavků a zpráv. Navíc vznik souběžného programování vyžadoval, aby se chyby zpracovávaly i v jiných vláknech než v hlavním.
Chyby se staly natolik složitými, že již nebylo možné je zpracovávat na jednom místě a žádné jediné místo nemohlo nést odpovědnost za všechny chyby. Právě zde se u try-catch objevuje závažný problém.
Přenášení odpovědnosti u try-catch
Try-catch je stručně řečeno způsob, jakým subjekt, který chybu vyvolal, přenáší odpovědnost za její důsledky (dořešení) na někoho jiného. Tímto někým může být blok catch, nadřazená metoda, nadřazená metoda nadřazené metody, nadřazená metoda… a tak dále. Jinými slovy, v世界 rostoucího počtu a složitosti chybových situací zvolil try-catch přístup „někdo to vyřeší“. Podívejme se na následující kód.
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}
Problém tohoto kódu spočívá v tom, že není jasné, který subjekt vlastně zpracovává printStackTrace, a také nelze zjistit, v jakém kódu chyba nastala. Čím více logiky obsahuje blok try, tím je problém horší. Paradoxně však čím složitější vývoj byl, tím více se vývojáři upínali k try-catch, který odpovědnost přenáší, nezabývali se zpracováním chyb, jejich smysl pro odpovědnost slábl a nakonec zapomněli na podstatu zpracování chyb a výjimek. Jak tedy golang tento problém vyřešil?
panic, recover
Jednou z výhod Go je to, že obsahuje řadu mechanismů, které vývojáře nenasměrují na špatnou cestu, ale pomáhají mu stát se dobrým vývojářem. Příkladem jsou právě panic a recover.
Panic a recover na první pohled nejsou od try-catch nijak odlišné,liší se však tím, že při vzniku problému nepřenášejí odpovědnost navenek. V Go, když nastane panika, se má řešit na místě, kde k ní došlo, a nikoli předávat její hodnota dál. To vývojáři svěřuje odpovědnost za zpracování chyby a vede ho k tomu, aby důkladněji uvažoval o tom, kde, kdo a jak má chybu zpracovat. Uživateli se tak poskytuje autonomie a zároveň se mu ponechává prostor pro přemýšlení.
Název panic je také velmi výstižný. Na rozdíl od try-recover si už samotné slovo panic-recover vynucuje, aby se tyto konstrukce používaly pouze v případech skutečné chybové situace, nikoli pouhé výjimky. Vývojář pak přirozeně nebude recover zneužívat a použije jej jen tam, kde je to nezbytné. To výrazně pomáhá psát v Go stručný kód.