Por que Go tem uma declaração “goto”

110

Fiquei surpreso ao descobrir que Go tem uma declaração 'goto' . Sempre fui ensinado que as declarações 'goto' são coisas do passado e más, pois obstruem o fluxo real de um programa, e que funções ou métodos são sempre uma maneira melhor de controlar o fluxo.

Eu devo estar esquecendo alguma coisa. Por que o Google o incluiu?

prejuízo
fonte
5
Há momentos em que você realmente precisa de uma instrução goto. Goto's são maléficos apenas quando usados ​​indiscriminadamente. Por exemplo, se for muito difícil, senão impossível, escrever um analisador de máquina de estado finito sem instruções goto.
xbonez
5
Não é específico do Go, mas para uma boa discussão sobre por que as línguas mantêm a declaração e para ver os argumentos contra seu uso, verifique este post . Existem algumas boas referências relacionadas na pergunta. Edit: aqui está outro .
Cᴏʀʏ
3
Para salvar o OP do grep nas discussões SO fornecidas, aqui está a discussão sobre LKML, que resume por que gotoé útil em certos casos. Leia depois de estudar a resposta de @ Kissaki.
kostix
1
Relacionado: programmers.stackexchange.com/q/566/33478 (e veja minha resposta ).
Keith Thompson
É útil implementar um padrão de continuação, em que você salva da pilha e volta para onde estava quando deseja continuar.
Justin Dennahower

Respostas:

78

Quando realmente verificamos o código-fonte da biblioteca padrão Go, podemos ver onde os gotos estão realmente bem aplicados.

Por exemplo, no math/gamma.goarquivo, a gotoinstrução é usada :

  for x < 0 {
    if x > -1e-09 {
      goto small
    }
    z = z / x
    x = x + 1
  }
  for x < 2 {
    if x < 1e-09 {
      goto small
    }
    z = z / x
    x = x + 1
  }

  if x == 2 {
    return z
  }

  x = x - 2
  p = (((((x*_gamP[0]+_gamP[1])*x+_gamP[2])*x+_gamP[3])*x+_gamP[4])*x+_gamP[5])*x + _gamP[6]
  q = ((((((x*_gamQ[0]+_gamQ[1])*x+_gamQ[2])*x+_gamQ[3])*x+_gamQ[4])*x+_gamQ[5])*x+_gamQ[6])*x + _gamQ[7]
  return z * p / q

small:
  if x == 0 {
    return Inf(1)
  }
  return z / ((1 + Euler*x) * x)
}

O gotoneste caso nos poupa de introduzir outra variável (booleana) usada apenas para controle de fluxo, verificada no final. Nesse caso , a gotoinstrução torna o código realmente melhor de ler e mais fácil de seguir (ao contrário do argumento contra que gotovocê mencionou).

Observe também que a gotoinstrução tem um caso de uso muito específico. A especificação da linguagem em goto afirma que ela não pode saltar sobre as variáveis ​​que entram no escopo (sendo declaradas) e não pode saltar para outros blocos (código-).

Kissaki
fonte
69
Em seu exemplo, por que não apenas introduzir uma função small(x,z)a ser chamada? Dessa forma, não precisamos pensar sobre quais variáveis ​​estão acessíveis no small:rótulo. Suspeito que o motivo seja go ainda carece de certos tipos de suporte embutido no compilador.
Thomas Ahle
5
@Jessta: É para isso que temos visibilidade e escopo, certo?
Thomas Ahle
6
@ThomasAhle Go não permite gotoapontar para um rótulo após a introdução de novas variáveis. Executar a instrução "goto" não deve fazer com que nenhuma variável entre no escopo que ainda não estivesse no escopo no ponto do goto.
km6zla
4
@ ogc-nick Desculpe, não fui claro, eu quis dizer que as funções podem ser declaradas no escopo onde são necessárias, portanto, não são visíveis ao código que não precisa delas. Eu não estava falando sobre goto's e alcance.
Thomas Ahle
4
@MosheRevah O código referenciado não é otimizado para legibilidade. Ele é otimizado para desempenho bruto, usando um goto que abrange 22 linhas em uma única função. (E a proposta de Thomas Ahle é ainda mais legível aos meus olhos.)
joel.neely
30

Goto é uma boa ideia quando nenhum dos recursos de controle integrados faz exatamente o que você deseja e quando você pode expressar o que deseja com um goto. (É uma pena, nesses casos, em alguns idiomas, quando você não tem um goto. Você acaba abusando de algum recurso de controle, usando sinalizadores booleanos ou usando outras soluções piores do que goto.)

Se algum outro recurso de controle (usado de maneira razoavelmente óbvia) pode fazer o que você deseja, você deve usá-lo em preferência para ir para. Se não, seja ousado e use goto!

Finalmente, é importante notar que goto de Go tem algumas restrições destinadas a evitar alguns bugs obscuros. Veja essas restrições nas especificações.

Sonia
fonte
7

As declarações de Goto receberam muito descrédito desde a era do código Spaghetti nos anos 60 e 70. Naquela época, a metodologia de desenvolvimento de software era muito pobre ou inexistente. No entanto, Goto não é nativamente mau, mas pode, claro, ser mal usado e abusado por programadores preguiçosos ou não qualificados. Muitos problemas com Gotos abusados ​​podem ser resolvidos com processos de desenvolvimento, como revisões de código de equipe.

gotosão saltos da mesma maneira técnica que continue, breake return. Alguém poderia argumentar que essas afirmações são más da mesma maneira, mas não são.

A razão pela qual a equipe Go incluiu o Gotos é provavelmente devido ao fato de que é um primitivo de controle de fluxo comum. Além disso, eles esperançosamente concluíram que o escopo do Go exclui tornar uma linguagem segura para idiotas impossível de ser abusada.

Gustav
fonte
continue,, breake returnsão muito diferentes em uma chave particular: eles especificam apenas "deixar o escopo delimitador". Eles não apenas encorajam, mas explicitamente exigem que o desenvolvedor considere a estrutura de seu código e confie em primitivas de programação estruturadas (para loops, funções e instruções switch). A única graça das gotoinstruções é que elas permitem que você grave assembly em um HLL quando o otimizador do compilador não está à altura da tarefa, mas isso tem um custo de legibilidade e manutenção.
Parthian Shot
A razão que esta é tão surpreendente encontrar em movimento é que, em todos os outros casos em que os desenvolvedores golang tinha uma escolha entre paradigma de programação estruturada e "controle de fluxo excepcional" / manipulações ponteiro de instrução como setjmp, longjmp, goto, e try / except / finallyeles escolheram para err no lado de cautela. goto, fwict, é a única aquiescência ao fluxo de controle de pré-"programação estruturada".
Parthian Shot