GoSuda

Por que Go optou pelo retorno explícito de erros e por panic-recover em vez do lançamento de exceções com try-catch

By Rabbit Princess views ...

Go deliberadamente não oferece suporte a try-catch e suporta apenas a sintaxe panic-recover. Isso tem gerado insatisfação entre os muitos desenvolvedores acostumados ao tratamento de erros com try-catch. Então, por que o try-catch não foi incluído? Isso ocorre porque o try-catch apresenta vários problemas.

try-catch-finally

A estrutura try-catch (try-catch) é uma construção destinada a tratar situações de erro e de exceção que podem ocorrer durante a execução do programa (em tempo de execução). Além disso, o bloco finally contém o código que deve ser executado incondicionalmente, independentemente da ocorrência de exceções.

Tratamento de Erros e Responsabilidade

Nas décadas de 1980 e 1990, o tratamento de erros era muito simples. As mensagens de erro se resumiam a "disquete cheio", "nenhum disquete na unidade" e "sem permissão de gravação no disquete". Nessa época, quando um erro ocorria, os desenvolvedores o propagavam (throw) até o ponto de tratamento de erros, centralizando o processamento. Nesse cenário, a estrutura try-catch funcionava de forma eficiente.

Contudo, com o passar do tempo, a situação mudou. A lógica de negócio tornou-se complexa, surgiram bancos de dados e transações, era preciso chamar inúmeras APIs pela rede e interpretar muitas mensagens de requisição e, até mesmo, com o advento da programação concorrente, tornou-se necessário tratar erros em threads diferentes da principal.

Os erros tornaram-se complexos demais para serem tratados em um único lugar, e nenhum ponto isolado passou a ser capaz de se responsabilizar por todos eles. É aqui que o try-catch passa a apresentar um problema grave.

A Transferência de Responsabilidade do try-catch

Em suma, o try-catch é um método pelo qual o sujeito que gera um erro transfere a responsabilidade pela sua ocorrência (o tratamento posterior) a outra parte. Essa parte pode ser o bloco catch, o método pai, o pai do pai do pai do pai... ou qualquer outro elemento. Em outras palavras, em um mundo onde o tratamento de erros se torna cada vez mais numeroso e complexo, a solução adotada pelo try-catch é simplesmente "alguém vai resolver". 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    e.printStackTrace();
8}

O problema desse código é que não fica claro qual é o responsável por processar o printStackTrace, e tampouco é possível saber em qual trecho o erro ocorreu. Além disso, quanto mais lógica houver dentro do bloco try, pior a situação se torna. Ainda assim, paradoxalmente, quanto mais complexo o desenvolvimento se tornava, mais os desenvolvedores se viciavam em estruturas try-catch que transferem responsabilidades; deixaram de refletir sobre o tratamento de erros, sua noção de responsabilidade se enfraqueceu e, por fim, esqueceram a essência do tratamento de erros e de exceções. Então, como o Golang resolveu esse problema?

panic e recover

Uma das vantagens da linguagem Go é possuir diversos mecanismos que, em vez de conduzir o desenvolvedor por caminhos equivocados, o ajudam a se tornar um bom desenvolvedor. O par panic-recover é um exemplo disso.

À primeira vista, panic e recover não parecem diferentes do try-catch, mas diferem no ponto de que, quando ocorre um problema, a responsabilidade não é transferida para o exterior. Em Go, quando ocorre um panic, o valor não é simplesmente repassado para fora; a solução deve ser feita no próprio local onde o panic ocorreu. Isso atribui ao desenvolvedor a responsabilidade pelo tratamento de erros, levando-o a refletir com mais profundidade sobre onde, por quem e como esse erro deve ser tratado. Trata-se de garantir autonomia ao usuário, ao mesmo tempo em que se deixa espaço para a reflexão.

Além disso, a escolha da palavra panic também é notavelmente acertada: diferentemente de try-recover, o par panic-recover impõe ao desenvolvedor a pressão de que deve ser usado apenas em situações de erro evidente, e não em situações meramente excepcionais. Naturalmente, o desenvolvedor não abusa de recover e o utiliza apenas onde é necessário. Isso contribui muito para a escrita de código conciso em Go.