As construções de fluxo de controle de nível superior tendem a corresponder aos conceitos no domínio do problema. Um if / else é uma decisão baseada em alguma condição. Um loop diz para executar alguma ação repetidamente. Até uma declaração de pausa diz "estávamos fazendo isso repetidamente, mas agora precisamos parar".
Uma declaração goto, por outro lado, tende a corresponder a um conceito no programa em execução, não no domínio do problema. Ele diz para continuar a execução em um ponto especificado no programa . Alguém que lê o código precisa inferir o que isso significa em relação ao domínio do problema.
É claro que todas as construções de nível superior podem ser definidas em termos de gotos e ramificações condicionais simples. Isso não significa que eles são apenas gotos disfarçados. Pense neles como gotos restritos - e são as restrições que os tornam úteis. Uma declaração de interrupção é implementada como um salto para o final do loop fechado, mas é melhor pensar em operar no loop como um todo.
Sendo tudo igual, o código cuja estrutura reflete a do domínio do problema tende a ser mais fácil de ler e manter.
Não há casos em que uma declaração goto seja absolutamente necessária (há um teorema nesse sentido), mas há casos em que pode ser a solução menos ruim. Esses casos variam de idioma para idioma, dependendo das construções de nível superior que o idioma suporta.
Em C, por exemplo, acredito que há três cenários básicos em que um goto é apropriado.
- Rompendo um loop aninhado. Isso seria desnecessário se o idioma tivesse uma declaração de interrupção rotulada.
- Resgate de um trecho de código (normalmente um corpo de função) em caso de erro ou outro evento inesperado. Isso seria desnecessário se o idioma tivesse exceções.
- Implementando uma máquina explícita de estados finitos. Nesse caso (e, creio, apenas neste caso), um goto corresponde diretamente a um conceito no domínio do problema, passando de um estado para outro especificado, onde o estado atual é representado pelo bloco de código em execução no momento .
Por outro lado, uma máquina explícita de estados finitos também pode ser implementada com uma instrução switch dentro de um loop. Isso tem a vantagem de que todo estado inicia no mesmo local no código, o que pode ser útil para depuração, por exemplo.
O principal uso de um goto em uma linguagem razoavelmente moderna (que suporta if / else e loops) é simular uma construção de fluxo de controle que está faltando na linguagem.