GoSuda

Por que Go não possui Try-Catch?

By Rabbit Princess
views ...

Go intencionalmente não oferece suporte a try-catch, suportando apenas a sintaxe panic-recover. Isso tem gerado descontentamento entre muitos desenvolvedores acostumados ao tratamento de erros via try-catch. Então, qual seria o motivo para não incluir try-catch? A razão é que try-catch apresenta diversas problemáticas.

try-catch-finally

A construção try-catch é utilizada para gerenciar situações de erro e exceção que podem ocorrer durante a execução de um programa (runtime). Adicionalmente, a construção finally é empregada para inserir código que deve ser executado independentemente da ocorrência de uma exceção.

Tratamento de Erros e Responsabilidade

Nas décadas de 80 e 90, o tratamento de erros era consideravelmente simples. As mensagens de erro limitavam-se a 'Disquete cheio', 'Nenhum disquete na unidade', 'Permissão de gravação negada para o disquete', e, nessa época, os desenvolvedores, ao se depararem com um erro, o lançavam (throw) até o ponto de tratamento para que fosse processado de forma unificada. Nessas circunstâncias, a construção try-catch operava eficientemente.

Entretanto, com o passar do tempo, o cenário modificou-se. A lógica de negócios tornou-se mais complexa, bancos de dados e transações emergiram, e a necessidade de interpretar inúmeras mensagens de requisição ao invocar diversas APIs via rede surgiu; ademais, com o advento da programação concorrente, tornou-se imperativo tratar erros em threads distintas da principal.

Os erros tornaram-se excessivamente complexos para serem tratados em um único local, e nenhuma entidade isolada podia assumir a total responsabilidade por todos eles. É neste ponto que um problema significativo com try-catch se manifesta.

Delegação de Responsabilidade do try-catch

try-catch é, em suma, um método pelo qual a entidade que causou o erro delega a responsabilidade (o tratamento) a outra. Esse destinatário da delegação pode ser a construção catch, seu método pai, ou o pai do pai do pai do pai... de alguém. Em outras palavras, em um mundo onde o tratamento de erros se tornou mais numeroso e complexo, o método adotado por try-catch foi 'alguém fará isso'. Observe o código abaixo.

1try {
2    data = readFile("hello.txt");
3    structuredData = parseData(data);
4    insertDBStatus(structuredData[1]);
5    startHTTPServer(structuredData[2]);
6} catch (Exception e) {
7    // Imprime o stack trace da exceção.
8    e.printStackTrace();
9}

O problema com o código acima é que não fica claro qual entidade é responsável por processar printStackTrace, nem é possível determinar onde o erro ocorreu no código. Além disso, quanto mais lógica é inserida na instrução try, mais terrível o problema se torna. Contudo, paradoxalmente, à medida que o desenvolvimento se tornava mais complexo, os desenvolvedores se viciaram na construção try-catch, que delega responsabilidades, negligenciando a reflexão sobre o tratamento de erros, e a consciência de responsabilidade diminuiu, resultando no esquecimento da essência do tratamento de erros e exceções. Então, como o golang resolveu esse problema?

panic, recover

Uma das vantagens do Go é a existência de diversos sistemas que visam guiar o desenvolvedor para boas práticas, em vez de o desviar para caminhos inadequados. panic-recover é um exemplo disso.

À primeira vista, panic e recover podem não parecer diferentes de try-catch, mas distinguem-se por não transferir a responsabilidade para o exterior quando um problema ocorre. Em Go, quando um panic acontece, a resolução deve ocorrer no local onde o panic foi acionado, em vez de o valor ser transferido para fora. Isso atribui ao desenvolvedor a responsabilidade pelo tratamento de erros, incentivando uma reflexão mais aprofundada sobre onde, quem e como o erro deve ser tratado. Assim, é concedida autonomia ao usuário, ao mesmo tempo em que se deixa margem para a reflexão.

Além disso, a escolha da palavra "panic" é notavelmente perspicaz. Diferentemente de try-recover, panic-recover impõe ao desenvolvedor a pressão, apenas pela palavra, de que deve ser utilizado unicamente em situações de erro explícitas, e não em meras exceções. Consequentemente, o desenvolvedor evita o uso excessivo de recover, empregando-o apenas onde é necessário. Isso contribui significativamente para a elaboração de código conciso em Go.