Controle de fluxo: if, else e inicialização inline
1. O if tradicional em Go
Em Go, a estrutura condicional if segue uma sintaxe limpa e direta, sem os parênteses obrigatórios encontrados em linguagens como C, Java ou JavaScript. As chaves, no entanto, são obrigatórias — mesmo para blocos de uma única linha.
package main
import "fmt"
func main() {
idade := 18
if idade >= 18 {
fmt.Println("Maior de idade")
}
}
Diferente de Python, que usa indentação, ou de C/Java, que exigem parênteses, Go opta por uma abordagem que reduz ruído visual sem abrir mão da clareza estrutural. As chaves delimitam claramente o escopo, evitando ambiguidades.
2. O else e else if
A estrutura else e else if segue regras rigorosas de formatação. O else deve estar na mesma linha da chave de fechamento do if, e a chave de abertura do else deve vir na mesma linha.
package main
import "fmt"
func main() {
nota := 7.5
if nota >= 7 {
fmt.Println("Aprovado")
} else if nota >= 5 {
fmt.Println("Recuperação")
} else {
fmt.Println("Reprovado")
}
}
Essa exigência — que o else esteja na mesma linha da chave de fechamento — é imposta pelo formatador oficial gofmt. Isso garante consistência em todo código Go e evita discussões sobre estilo.
3. Inicialização inline (if com statement)
Um dos recursos mais elegantes de Go é a capacidade de declarar uma variável dentro da própria condição do if. A sintaxe é: if statement; condition { }.
package main
import (
"fmt"
"strconv"
)
func main() {
valor := "42"
if num, err := strconv.Atoi(valor); err == nil {
fmt.Printf("Número convertido: %d\n", num)
} else {
fmt.Printf("Erro na conversão: %v\n", err)
}
}
A variável num declarada no statement tem seu escopo limitado ao bloco if e seus else associados. Isso é particularmente útil para capturar resultados de funções que retornam erro, como operações de I/O, parsing ou chamadas HTTP.
Comparação com declaração prévia
Sem inicialização inline, o código equivalente seria:
num, err := strconv.Atoi(valor)
if err != nil {
fmt.Printf("Erro: %v\n", err)
return
}
fmt.Printf("Número: %d\n", num)
A versão inline mantém o escopo mais restrito, evitando que variáveis temporárias "vazem" para o restante da função.
4. Escopo e shadowing de variáveis
Variáveis declaradas no statement do if não são acessíveis fora do bloco. Isso pode causar shadowing (sombreamento) se houver uma variável com o mesmo nome no escopo externo.
package main
import "fmt"
func main() {
x := 10
if x := 5; x > 0 { // shadowing: x do if esconde o x externo
fmt.Println("Dentro do if:", x) // 5
}
fmt.Println("Fora do if:", x) // 10
}
Esse comportamento pode ser útil em alguns casos, mas também pode gerar bugs sutis. A recomendação é evitar reutilizar nomes de variáveis em diferentes escopos dentro da mesma função.
5. Condições booleanas e operadores lógicos
Go suporta os operadores lógicos padrão: && (E), || (OU) e ! (NÃO). O operador && tem precedência maior que ||, e ambos utilizam short-circuit evaluation — a avaliação da expressão para assim que o resultado é determinado.
package main
import "fmt"
func main() {
usuario := "admin"
senha := "1234"
if usuario == "admin" && senha == "1234" {
fmt.Println("Acesso permitido")
}
// Short-circuit: se usuario for vazio, não avalia senha
if usuario != "" && senha != "" {
fmt.Println("Credenciais fornecidas")
}
}
A combinação de inicialização inline com operadores lógicos é poderosa:
if arquivo, err := os.Open("dados.txt"); err == nil && arquivo != nil {
defer arquivo.Close()
// processa arquivo
}
6. if com múltiplas condições e funções
É comum chamar funções diretamente na condição do if. Go permite isso naturalmente, desde que a função retorne um valor booleano.
package main
import (
"fmt"
"strings"
)
func contemPalavra(texto, palavra string) bool {
return strings.Contains(texto, palavra)
}
func main() {
frase := "Go é uma linguagem eficiente"
if contemPalavra(frase, "Go") {
fmt.Println("A frase menciona Go")
}
}
Aninhamento e early return
Aninhar muitos if/else prejudica a legibilidade. Go incentiva o padrão early return: verificar condições de erro primeiro e retornar imediatamente.
// Ruim: muito aninhamento
func processarDados(dados []byte) error {
if dados != nil {
if len(dados) > 0 {
if valido := validar(dados); valido {
// processa...
return nil
}
}
}
return fmt.Errorf("dados inválidos")
}
// Bom: early return
func processarDadosMelhor(dados []byte) error {
if dados == nil {
return fmt.Errorf("dados nulos")
}
if len(dados) == 0 {
return fmt.Errorf("dados vazios")
}
if !validar(dados) {
return fmt.Errorf("dados inválidos")
}
// processa...
return nil
}
7. Casos especiais e boas práticas
Type assertion com if
Para verificar tipos em interfaces, Go oferece a type assertion dentro do if:
var valor interface{} = "texto"
if str, ok := valor.(string); ok {
fmt.Println("É uma string:", str)
}
Preferir if a switch para poucos casos
Para 2-3 condições, if/else if é mais legível que switch.
Evitar else desnecessário
Quando um bloco if termina com return (ou break, continue), o else se torna redundante:
// Ruim
if err != nil {
return err
} else {
fmt.Println("Sucesso")
}
// Bom
if err != nil {
return err
}
fmt.Println("Sucesso")
Esse padrão reduz aninhamento e torna o fluxo mais linear.
O controle de fluxo com if em Go é simples, mas poderoso. A inicialização inline, o escopo restrito e a ênfase em código claro e direto são características que tornam a linguagem produtiva e fácil de manter. Dominar esses conceitos é essencial para escrever código Go idiomático e eficiente.
Referências
- A Tour of Go: If and else — Tutorial interativo oficial sobre if/else em Go, com exemplos práticos.
- Effective Go: If — Documentação oficial com boas práticas e padrões de uso do if em Go.
- Go by Example: If/Else — Exemplos concisos e comentados de if/else e inicialização inline.
- The Go Blog: Defer, Panic, and Recover — Artigo que aborda fluxo de controle avançado, incluindo uso de if com tratamento de erros.
- Go Specification: If statements — Especificação formal da linguagem sobre a construção if, incluindo a sintaxe de inicialização inline.