Usando goto e label em Go
A instrução goto tem fama negativa. No tempo do BASIC programadores não tinham muitas opções e abusavam do comando goto e criaram códigos confusos. Linguagens modernas, como Go, usam goto com critério e melhoram a clareza do código.
Go permite que os comandos break e continue usem labels. Use labels para sair de loops aninhados com break dar continue em um loop específico. Usar break ou continue com labels equivale a usar goto.
Exemplo de uso de break com label
Neste exemplo, o comando break sai dos dois loops aninhados.
label:
for coluna := 10; coluna > 0; coluna-- {
for linha := 10; linha > 0; linha-- {
fmt.Println(linha, coluna)
if linha == 5 {
break label
}
}
}
Exemplo de uso de continue com label
Neste exemplo, o comando continue pula o loop interno e continua o loop externo.
label:
for coluna := 10; coluna > 0; coluna-- {
for linha := 10; linha > 0; linha-- {
fmt.Println(linha, coluna)
if linha == 5 {
continue label
}
}
}
Exemplo de uso de goto com label
Neste exemplo, o comando goto cria um loop infinito. O loop interrompe quando o canal messages recebe uma mensagem ou quando o tempo de 2 segundos expira.
func main() {
timeout := time.After(2 * time.Second)
messages := make(chan string)
go func() { messages <- "ping" }()
loop:
select {
case <-timeout:
fmt.Println("timeout")
os.Exit(1)
case m := <-messages:
fmt.Println(m)
goto loop
}
}
Exemplo simples de goto com label
Neste exemplo, o comando goto pula instruções intermediárias.
func main() {
fmt.Println("1")
goto label
fmt.Println("2") // esta linha nunca será executada
label:
fmt.Println("3")
}
Exemplo de erro ao usar goto
Neste exemplo, o comando goto falha. O comando pula a declaração de uma variável, o que não é permitido.
fmt.Println("1")
goto label
var i = 1
fmt.Println("2", i)
label:
fmt.Println("3")
Veja os exemplos de uso de goto com Go.
Regras para usar labels
As regras para usar goto, continue e break em Go são:
- O programa não pode saltar para fora do escopo da função.
- O programa não pode saltar sobre a declaração de uma variável.
- O comando break com label exige que a instrução imediatamente após o label seja um for, switch ou select.
- O comando continue com label exige que a instrução imediatamente após o label seja um for.
Nada de errado com goto
O assembly não entende abstrações. O processador executa saltos para endereços de memória. O compilador converte estruturas de controle (if, for, goto etc.) em variações da instrução jmp que efetivamnte é um goto. O programador escolhe usar ou não os recursos mas ele esta lá quer ele queira ou não.
Bonus um padtão de uso de goto em C
Em linguagens que não possuem defer como C, o comando goto pode ser usado para liberar recursos antes de sair de uma função. Por exemplo, no lugar de simplesmente comandar um return na função, o programador comanda um salto goto para label em que ele libera os recursos e daí sai.
void funcao() {
FILE *f = fopen("arquivo.txt", "r");
if (f == NULL) {
goto erro;
}
// código que usa o arquivo
fclose(f);
return;
erro:
perror("Erro ao abrir o arquivo");
exit(1);
}
Conclusão
O comando goto é uma ferramenta poderosa. Devemos usá-lo com critério e seguir boas práticas de programação mas não se restringir porque ouviu falar que goto é ruim. O comando goto é uma ferramenta útil e deve ser usada quando necessário.
Vídeo com a explicação do artigo.