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