De ce a ales Go returnarea explicită a erorilor și panic-recover în locul aruncării excepțiilor prin try-catch

Go intenționat nu oferă suport pentru try-catch, ci doar pentru construcția panic-recover. Acest lucru a stârnit nemulțumirea numeroșilor dezvoltatori obișnuiți cu tratarea erorilor prin try-catch. Atunci, de ce nu a fost inclus try-catch? Motivul este că try-catch prezintă mai multe probleme.
try-catch-finally
Construcția try-catch (try-catch) reprezintă o structură destinată tratării situațiilor de eroare și a condițiilor excepționale care pot apărea pe durata execuției programului (runtime). Blocul finally, pe de altă parte, conține codul care trebuie executat în mod obligatoriu, indiferent dacă a apărut sau nu o excepție.
Tratarea erorilor și responsabilitatea
În anii 1980 și 1990, tratarea erorilor era foarte simplă. Mesajele de eroare se limitau la „discheta plină", „nu există discheta în unitate" și „nu există permisiune de scriere pe discheta", iar dezvoltatorii din acea epocă, atunci când apărea o eroare, o propagau prin throw până la punctul de tratare comun, unde era gestionată centralizat. În acest context, construcția try-catch funcționa eficient.
Totuși, odată cu trecerea timpului, situația s-a schimbat. Logica de business a devenit complexă, au apărut bazele de date și tranzacțiile, iar prin rețea s-au apelat numeroase API-uri, fiind necesar să se interpreteze un mare număr de mesaje de cerere. Mai mult, odată cu apariția programării concurente, erorile trebuiau uneori tratate în alte fire de execuție decât cel principal.
Erorile au devenit prea complexe pentru a mai putea fi tratate într-un singur loc, iar niciun punct unic nu mai putea răspunde pentru toate erorile. Aici apare o problemă serioasă a try-catch.
Delegarea responsabilității în try-catch
Pe scurt, try-catch este o metodă prin care entitatea care generează eroarea deleagă responsabilitatea pentru eroare (operațiunile de curățare ulterioare) altcuiva. Acest „altcineva" poate fi blocul catch, metoda părinte, metoda părinte a părintelui, a părintelui părintelui părintelui părintelui... sau oricine altcineva. Cu alte cuvinte, într-o lume în care tratarea erorilor devine tot mai numeroasă și mai complexă, abordarea adoptată de try-catch este tocmai „cineva se va ocupa de asta". Să analizăm codul de mai jos.
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}
Problema acestui cod este că nu este clar ce entitate se ocupă de printStackTrace și nici nu se poate stabili în ce parte a codului a apărut eroarea. Mai mult, pe măsură ce blocul try conține mai multă logică, problema devine din ce în ce mai gravă. Totuși, paradoxal, pe măsură ce dezvoltarea devenea tot mai complexă, dezvoltatorii s-au obișnuit cu try-catch, care deleagă responsabilitatea, au încetat să mai reflecteze asupra tratării erorilor, responsabilitatea lor s-a diminuat și, în cele din urmă, au uitat esența tratării erorilor și a excepțiilor. Atunci, cum a rezolvat golang această problemă?
panic și recover
Unul dintre avantajele limbajului Go este faptul că dispune de mai multe mecanisme care îi ajută pe dezvoltatori să nu apuce pe căi greșite, ci să devină dezvoltatori buni. Panic și recover reprezintă un exemplu în acest sens.
La prima vedere, panic și recover nu par diferite de try-catch, însă se deosebesc prin faptul că, în cazul unei probleme, responsabilitatea nu este transferată către exterior. În Go, atunci când apare un panic, valoarea respectivă nu este trimisă în afară, ci problema trebuie rezolvată chiar în locul unde a apărut panic-ul. Aceasta îi atribuie dezvoltatorului responsabilitatea tratării erorii și îl determină să reflecteze mai profund asupra locului, persoanei și modului în care trebuie tratată eroarea respectivă. Este, în fond, o modalitate de a oferi autonomie utilizatorului, lăsându-i totodată spațiu pentru gândire.
De asemenea, alegerea cuvântului panic este foarte inspirată. Spre deosebire de try-recover, perechea panic-recover îi transmite dezvoltatorului presiunea de a o folosi doar în situații de eroare evidentă, nu în simple situații excepționale. Astfel, dezvoltatorul nu abuzează de recover și îl utilizează numai acolo unde este necesar. Acest lucru contribuie considerabil la scrierea unui cod concis în Go.