Prečo Go namiesto vyhadzovania výnimiek pomocou try-catch zvolil explicitné vracanie chýb a panic-recover

Go je zámerne nepodporuje try-catch a podporuje iba syntax panic-recover. To vyvoláva nevôľu mnohých vývojárov, ktorí sú zvyknutí na spracovanie chýb pomocou try-catch. Prečo teda Go nezaviedol try-catch? Dôvodom je, že try-catch má viacero problémov.
try-catch-finally
Štruktúra try-catch (try-catch) slúži na spracovanie chybových a výnimočných situácií, ktoré môžu nastať počas behu programu (za behu, runtime). Blok finally obsahuje kód, ktorý sa musí vykonať bezpodmienečne, bez ohľadu na to, či došlo k výnimke.
Spracovanie chýb a zodpovednosť
V 80. a 90. rokoch bolo spracovanie chýb veľmi jednoduché. Chybové hlásenia boli iba „Disketa je plná“, „V jednotke nie je disketa“ a „Nemáte oprávnenie na zápis na disketu“, a vývojári v tej dobe pri vzniku chyby odovzdávali chybu (throw) až na miesto, kde sa spracovávala, čím sa spracovanie centralizovalo. V takejto situácii fungovala štruktúra try-catch efektívne.
Časom sa však situácia zmenila. Obchodná logika sa stala zložitejšou, pribudli databázy a transakcie, prostredníctvom siete sa volalo množstvo API a bolo potrebné interpretovať mnoho požiadaviek, a dokonca vďaka vzniku súbežného programovania bolo potrebné spracovávať chyby aj vo vláknach iných ako hlavné.
Chyby sa už nedali spracovať na jednom mieste a žiadne jediné miesto už nemohlo niesť zodpovednosť za všetky chyby. Práve tu nastal u try-catch vážny problém.
Prenášanie zodpovednosti pri try-catch
Try-catch je, stručne povedané, spôsob, akým subjekt, ktorý chybu spôsobil, prenecháva zodpovednosť za jej dôsledky (dočistenie) niekomu inému. Týmto niekým môže byť blok catch, nadradená metóda, nadradená metóda nadradenej metódy, nadradená metóda... alebo ktokoľvek ďalší. Inými slovami, v svete, kde spracovanie chýb rastie a komplikuje sa, zvolil try-catch riešenie „niekto to už urobí“. Pozrite sa na nasledujúci 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 tohto kódu spočíva v tom, že nie je jasné, kto je zodpovedný za spracovanie printStackTrace, a nie je možné zistiť, v ktorom kóde chyba vznikla. Ak sa v bloku try nachádza ešte viac logiky, problém je ešte väčší. Paradoxne však, čím zložitejší vývoj bol, tým viac vývojári prepadali try-catch, ktorý zodpovednosť prenáša, nevenovali pozornosť spracovaniu chýb, ich zodpovednostné vedomie oslabovalo a nakoniec zabudli na podstatu spracovania chýb a výnimiek. Ako si s týmto problémom poradil golang?
panic, recover
Jednou z výhod jazyka Go je, že obsahuje viacero systémov, ktoré vývojárov neodvádzajú od správnej cesty a pomáhajú im stať sa dobrými vývojármi. Dobrým príkladom sú práve panic a recover.
Na prvý pohľad sa panic a recover od try-catch takmer nelíšia, líšia sa však v tom, že pri vzniku problému nepreniesú zodpovednosť na externú časť. V Go, keď nastane panic, sa jeho hodnota neodovzdáva navonok, ale spracovanie musí prebehnúť na mieste, kde panic vznikol. To vývojárovi zveruje zodpovednosť za spracovanie chyby a núti ho dôkladnejšie premýšľať o tom, kde, kto a ako má chybu spracovať. Ide o to, že používateľovi zaručuje autonómiu a zároveň mu necháva priestor na premýšľanie.
Zvolenie názvu panic je tiež mimoriadne výstižné. Na rozdiel od try-recover podporuje dvojica panic-recover už samotným názvom predstavu, že sa má používať iba v prípade skutočne závažnej chybovej situácie, nie pri bežnej výnimočnej situácii. Vývojár je tak prirodzene nútený nezneužívať recover a používať ho iba tam, kde je to nevyhnutné. To výrazne pomáha pri písaní stručného kódu v Go.