GoSuda

Proč Go nemá Try-Catch?

By Rabbit Princess
views ...

go záměrně nepodporuje try-catch a podporuje pouze syntaxi panic-recover. To vyvolává nevoli u mnoha vývojářů zvyklých na zpracování chyb pomocí try-catch. Proč tedy try-catch nebylo přidáno? Je to proto, že try-catch má několik problémů.

try-catch-finally

Konstrukce try-catch je určena pro zpracování chybových a výjimečných situací, které mohou nastat během provádění programu (za běhu). Klauzule finally pak obsahuje kód, který se musí provést bez ohledu na to, zda došlo k výjimce.

Zpracování chyb a odpovědnost

V 80. a 90. letech bylo zpracování chyb velmi jednoduché. Chybové zprávy se omezovaly na 'Floppy disk plný', 'V mechanice není disketa', 'Nemáte oprávnění k zápisu na disketu', a vývojáři v té době, pokud došlo k chybě, tuto chybu vyhodili až k místu zpracování chyb a zpracovali ji společně. V takové situaci fungovala konstrukce try-catch efektivně.

Časem se však situace změnila. Obchodní logika se stala složitější, objevily se databáze a transakce, a bylo nutné interpretovat nesčetné zprávy požadavků při volání mnoha API přes síť. Dokonce i s příchodem souběžného programování bylo nutné zpracovávat chyby v jiných vláknech než v hlavním.

Chyby se staly natolik složitými, že je nebylo možné zpracovávat na jednom místě, a nikdo nemohl nést odpovědnost za všechny chyby. Zde nastává vážný problém s try-catch.

Delegování odpovědnosti v try-catch

try-catch je, jednoduše řečeno, způsob, jakým subjekt, který chybu způsobil, přenáší odpovědnost (následné zpracování) za vznik chyby na někoho jiného. Tímto subjektem může být klauzule catch, nadřazená metoda, nebo nadřazený nadřazeného nadřazeného... Někdo. Jinými slovy, ve světě, kde se zpracování chyb stává hojnějším a složitějším, try-catch volí metodu 'někdo to udělá'. 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 s výše uvedeným kódem je, že není jasné, kdo zpracovává printStackTrace, a není možné zjistit, ve které části kódu došlo k chybě. Čím více logiky se objeví uvnitř příkazu try, tím hroznější se problém stává. Paradoxně však platí, že čím složitější se vývoj stával, tím více se vývojáři stali závislými na konstrukci try-catch, která deleguje odpovědnost, přestali se zabývat zpracováním chyb, jejich smysl pro odpovědnost se oslabil, a nakonec zapomněli na podstatu zpracování chyb a výjimek. Jak tedy tento problém vyřešil golang?

panic, recover

Jednou z výhod go je, že má různé systémy, které pomáhají vývojářům stát se dobrými vývojáři, aniž by se vydali špatnou cestou. Příkladem je i panic - recover. ​ Panic a recover se na první pohled neliší od try-catch, ale liší se tím, že při problému nepřenášejí odpovědnost ven. Když v go dojde k panic, místo aby se hodnota přenášela ven, měla by se řešit v místě, kde k panic došlo. To dává vývojáři odpovědnost za zpracování chyb a nutí ho hlouběji přemýšlet o tom, kde, kdo a jak by se měla chyba zpracovat. Poskytuje uživateli autonomii a zároveň ponechává prostor k zamyšlení.

Navíc výběr slova "panic" je velmi vynikající, protože na rozdíl od try-recover, panic-recover samotným slovem dává vývojáři tlak, že by se mělo používat pouze v jasných chybových situacích, nikoli ve výjimečných situacích. Přirozeně tak vývojáři recover nezneužívají a používají ho pouze tam, kde je to nutné. To výrazně pomáhá při psaní stručného kódu v go.