GoSuda

Perché Go non ha Try-Catch?

By Rabbit Princess
views ...

go intenzionalmente non supporta try-catch, ma solo la sintassi panic-recover. Ciò ha suscitato le lamentele di numerosi sviluppatori abituati alla gestione degli errori tramite try-catch. Qual è, allora, il motivo per non includere try-catch? La ragione è che try-catch presenta diversi problemi.

try-catch-finally

La struttura try-catch è una costruzione sintattica progettata per gestire situazioni di errore e di eccezione che possono verificarsi durante l'esecuzione (runtime) di un programma. Inoltre, la clausola finally viene utilizzata per racchiudere il codice che deve essere eseguito in ogni caso, indipendentemente dal verificarsi o meno di un'eccezione.

Gestione degli errori e responsabilità

Negli anni '80 e '90, la gestione degli errori era estremamente semplice. I messaggi di errore si limitavano a 'Floppy disk pieno', 'Nessun floppy disk nell'unità', 'Nessun permesso di scrittura sul floppy disk', e gli sviluppatori di quel periodo gestivano gli errori generati propagandoli (throw) fino a un punto comune di gestione. In tale contesto, la struttura try-catch funzionava in modo efficiente.

Tuttavia, con il passare del tempo, la situazione è mutata. La logica di business è divenuta più complessa, sono stati introdotti database e transazioni, è stato necessario interpretare innumerevoli messaggi di richiesta chiamando numerose API tramite la rete e, persino, con l'avvento della programmazione concorrente, è diventato indispensabile gestire gli errori in thread diversi da quello principale.

Gli errori sono divenuti troppo complessi per essere gestiti in un unico luogo, e nessuna entità singola poteva più assumersi la responsabilità di tutti gli errori. È qui che emerge un grave problema con try-catch.

Trasferimento della responsabilità di try-catch

try-catch è, in sintesi, un metodo con cui l'entità che ha causato l'errore trasferisce la responsabilità (la gestione successiva) a qualcun altro. Questo destinatario può essere la clausola catch, il metodo genitore, o il genitore del genitore del genitore del genitore... di qualcuno. In altre parole, in un mondo in cui la gestione degli errori è sempre più estesa e complessa, l'approccio adottato da try-catch è "qualcuno se ne occuperà". Si consideri il seguente codice.

1try {
2    data = readFile("hello.txt"); // Legge il file "hello.txt"
3    structuredData = parseData(data); // Analizza i dati letti
4    insertDBStatus(structuredData[1]); // Inserisce lo stato nel DB
5    startHTTPServer(structuredData[2]); // Avvia il server HTTP
6} catch (Exception e) {
7    e.printStackTrace(); // Stampa la traccia dello stack dell'eccezione
8}

Il problema di questo codice è che non è chiaro chi sia responsabile della gestione di printStackTrace, né da quale riga di codice sia scaturito l'errore. Inoltre, più logica viene inserita all'interno del blocco try, più la situazione diventa terribile. Paradossalmente, tuttavia, quanto più lo sviluppo si complica, tanto più gli sviluppatori sono diventati dipendenti dalla struttura try-catch che trasferisce la responsabilità, trascurando la riflessione sulla gestione degli errori, attenuando il senso di responsabilità e, in ultima analisi, dimenticando l'essenza della gestione degli errori e delle eccezioni. Come ha risolto questo problema golang?

panic, recover

Uno dei vantaggi di Go è la presenza di vari sistemi volti a guidare gli sviluppatori verso pratiche ottimali, piuttosto che indurli a percorsi errati. panic-recover ne è un esempio. ​ A prima vista, panic e recover potrebbero apparire simili a try-catch, ma si distinguono per il fatto che, in caso di problema, la responsabilità non viene trasferita all'esterno. Quando si verifica un panic in Go, la risoluzione deve avvenire nel punto in cui si è verificato il panic, piuttosto che propagare il valore all'esterno. Ciò attribuisce allo sviluppatore la responsabilità della gestione degli errori, inducendolo a considerare in modo più approfondito dove, chi e come l'errore debba essere gestito. In tal modo, si garantisce autonomia all'utente, lasciando al contempo spazio alla riflessione.

Inoltre, la scelta del termine "panic" è estremamente azzeccata; a differenza di try-recover, panic-recover impone agli sviluppatori, già solo attraverso la parola, la pressione di doverlo utilizzare esclusivamente in situazioni di errore evidenti e non in semplici eccezioni. Di conseguenza, gli sviluppatori evitano di abusare di recover, impiegandolo solo dove strettamente necessario. Ciò contribuisce significativamente alla stesura di codice conciso in Go.