O que é If em Go?

A instrução if em Go é a estrutura fundamental de controle de fluxo condicional. Ela avalia uma expressão booleana e executa um bloco de código se a condição for verdadeira. Go adiciona uma característica poderosa ao if tradicional: a init statement (declaração de inicialização), que permite declarar e avaliar variáveis no mesmo escopo da condição — tornando o tratamento de erros mais conciso e seguro.

Diferente de linguagens como Python e JavaScript, o if em Go não usa parênteses ao redor da condição, mas exige chaves em todos os casos, mesmo para blocos de uma única linha. Essa decisão de design elimina uma classe inteira de bugs (como o famoso “goto fail” da Apple) e garante que todo código condicional seja visualmente consistente.

Para desenvolvedores que estão dando os primeiros passos em Go, o if é provavelmente a construção mais usada no dia a dia. Ele aparece em tratamento de erros, validação de entrada, verificação de tipos e praticamente toda lógica de decisão. Combinado com o padrão de early return, o if é a base do estilo idiomático de Go.

Sintaxe Básica do If

A sintaxe do if em Go é limpa e direta — condição sem parênteses, chaves obrigatórias:

idade := 18

if idade >= 18 {
    fmt.Println("Maior de idade")
}

If/Else

O bloco else deve estar na mesma linha que a chave de fechamento do if:

temperatura := 35

if temperatura > 30 {
    fmt.Println("Está muito quente!")
} else {
    fmt.Println("Temperatura agradável")
}

If/Else If/Else

Para múltiplas condições, encadeie com else if:

nota := 7.5

if nota >= 9.0 {
    fmt.Println("Conceito A")
} else if nota >= 7.0 {
    fmt.Println("Conceito B")
} else if nota >= 5.0 {
    fmt.Println("Conceito C")
} else {
    fmt.Println("Reprovado")
}

Note que em Go, o else if e else devem estar na mesma linha da chave de fechamento. O compilador exige essa formatação — go fmt aplica automaticamente.

Init Statement (Declaração de Inicialização)

O recurso mais poderoso do if em Go é a init statement: uma declaração curta executada antes da condição, cujas variáveis têm escopo limitado ao bloco if/else:

// Padrão idiomático de tratamento de erros
if err := json.Unmarshal(dados, &config); err != nil {
    log.Printf("Erro ao parsear config: %v", err)
    return err
}
// err não existe aqui — escopo limitado ao if

// Verificação de existência em map
if valor, ok := meuMap["chave"]; ok {
    fmt.Println("Encontrado:", valor)
} else {
    fmt.Println("Chave não encontrada")
}
// valor e ok não existem aqui

Esse padrão é um dos mais importantes de Go. A variável err (ou ok) existe apenas dentro do bloco if/else, mantendo o escopo limpo e evitando poluição de variáveis. Você encontra esse padrão em toda a biblioteca padrão e em praticamente todo código Go de produção.

Init Statement com Type Assertion

A init statement é especialmente útil com type assertions em interfaces:

var i interface{} = "Olá, Go!"

if s, ok := i.(string); ok {
    fmt.Println("É uma string:", s)
} else {
    fmt.Println("Não é uma string")
}

Init Statement com Channel Receive

Também funciona com operações de channel:

if msg, ok := <-meuChannel; ok {
    processar(msg)
} else {
    fmt.Println("Channel fechado")
}

O Padrão if err != nil

O padrão mais icônico de Go é o tratamento de erros com if err != nil. Ele aparece centenas de vezes em qualquer projeto Go de produção:

func carregarConfig(caminho string) (*Config, error) {
    dados, err := os.ReadFile(caminho)
    if err != nil {
        return nil, fmt.Errorf("falha ao ler arquivo: %w", err)
    }

    var config Config
    if err := json.Unmarshal(dados, &config); err != nil {
        return nil, fmt.Errorf("falha ao parsear JSON: %w", err)
    }

    if err := config.Validar(); err != nil {
        return nil, fmt.Errorf("configuração inválida: %w", err)
    }

    return &config, nil
}

Cada operação que pode falhar retorna um error que é verificado imediatamente. Esse fluxo explícito é intencional — Go prioriza clareza sobre conveniência. O uso de %w para wrap de erros permite verificação com errors.Is e errors.As posteriormente.

Guard Clauses (Early Return)

Guard clauses são o padrão recomendado em Go para validação de pré-condições. Em vez de aninhar múltiplos if/else, trate cada caso inválido com return imediato:

// Ruim: aninhamento profundo (pyramid of doom)
func processar(req *Request) (*Response, error) {
    if req != nil {
        if req.Body != nil {
            if req.Token != "" {
                // lógica principal
                return &Response{OK: true}, nil
            }
            return nil, errors.New("token ausente")
        }
        return nil, errors.New("body vazio")
    }
    return nil, errors.New("request nil")
}

// Bom: guard clauses com early return
func processar(req *Request) (*Response, error) {
    if req == nil {
        return nil, errors.New("request nil")
    }
    if req.Body == nil {
        return nil, errors.New("body vazio")
    }
    if req.Token == "" {
        return nil, errors.New("token ausente")
    }

    // Lógica principal no "happy path"
    return &Response{OK: true}, nil
}

O happy path fica alinhado à esquerda, sem aninhamento. Esse é o estilo recomendado pelo Go Code Review Comments e adotado por toda a biblioteca padrão.

If vs Switch

Quando você tem múltiplas condições sobre o mesmo valor, considere usar switch em vez de cadeias longas de if/else if:

// if/else if — verboso para muitas condições
if status == 200 {
    fmt.Println("OK")
} else if status == 404 {
    fmt.Println("Não encontrado")
} else if status == 500 {
    fmt.Println("Erro interno")
} else {
    fmt.Println("Status desconhecido")
}

// switch — mais limpo e legível
switch status {
case 200:
    fmt.Println("OK")
case 404:
    fmt.Println("Não encontrado")
case 500:
    fmt.Println("Erro interno")
default:
    fmt.Println("Status desconhecido")
}

Use if quando a condição é simples ou envolve expressões booleanas complexas. Use switch quando compara um valor contra múltiplas constantes ou quando precisa de type switch para interfaces.

If com Operadores Lógicos

Go suporta os operadores lógicos padrão && (AND), || (OR) e ! (NOT) nas condições:

// AND — ambas condições devem ser verdadeiras
if idade >= 18 && temCNH {
    fmt.Println("Pode dirigir")
}

// OR — pelo menos uma condição verdadeira
if role == "admin" || role == "superadmin" {
    fmt.Println("Acesso total")
}

// NOT — inverte a condição
if !usuario.Ativo {
    return errors.New("usuário inativo")
}

// Combinação
if (idade >= 18 && temDocumento) || acompanhadoPorResponsavel {
    fmt.Println("Entrada permitida")
}

Go avalia condições com short-circuit: em a && b, se a é falso, b não é avaliado. Em a || b, se a é verdadeiro, b não é avaliado. Isso é importante quando b tem efeitos colaterais ou pode causar panic com ponteiros nulos:

// Seguro: se usuario é nil, a segunda condição não é avaliada
if usuario != nil && usuario.Ativo {
    processar(usuario)
}

Idiomas Comuns com If em Go

Verificar nil antes de usar ponteiro

func nomeCompleto(p *Pessoa) string {
    if p == nil {
        return "Desconhecido"
    }
    return p.Nome + " " + p.Sobrenome
}

Comma-ok pattern com maps

cache := map[string]int{"go": 100, "python": 85}

if score, ok := cache["go"]; ok {
    fmt.Printf("Go score: %d\n", score)
}

Verificar tipo com type assertion

func descrever(i interface{}) string {
    if s, ok := i.(string); ok {
        return "string: " + s
    }
    if n, ok := i.(int); ok {
        return fmt.Sprintf("int: %d", n)
    }
    return "tipo desconhecido"
}

Boas Práticas com If

  1. Use init statement — mantenha variáveis temporárias com escopo limitado ao bloco if
  2. Prefira guard clauses — trate erros e casos inválidos com early return
  3. Evite else desnecessário — se o if termina com return, não precisa de else
  4. Use switch para múltiplas comparações — mais legível que cadeias longas de if/else if
  5. Mantenha condições simples — extraia condições complexas para variáveis ou funções nomeadas
  6. Aproveite short-circuit — coloque verificações de nil antes de acessar campos

Perguntas Frequentes (FAQ)

Por que Go não exige parênteses no if?

Go elimina parênteses ao redor da condição do if porque as chaves são obrigatórias — não há ambiguidade sintática. Isso resulta em código mais limpo e menos visual noise. O compilador Go é estrito: if (x > 0) funciona mas go fmt remove os parênteses automaticamente. Essa convenção garante que todo código Go tenha aparência consistente em qualquer projeto.

O que é a init statement do if em Go?

A init statement é uma declaração curta que precede a condição do if, separada por ponto e vírgula: if err := funcao(); err != nil {}. As variáveis declaradas na init statement têm escopo limitado ao bloco if/else, mantendo o namespace limpo. É o padrão idiomático para tratamento de erros e verificação de existência em maps com o comma-ok pattern.

Quando usar if vs switch em Go?

Use if para condições booleanas simples, verificações de erro, e guard clauses com early return. Use switch quando compara um valor contra três ou mais constantes, quando precisa de type switch para interfaces, ou quando a lógica se beneficia de múltiplos cases com múltiplos valores. A regra prática: se você tem mais de dois else if, considere migrar para switch.

Como funciona o short-circuit no if de Go?

O short-circuit (avaliação em curto-circuito) significa que Go para de avaliar uma expressão booleana assim que o resultado é determinado. Em a && b, se a é falso, b não é avaliado. Em a || b, se a é verdadeiro, b é ignorado. Isso é essencial para segurança: if ptr != nil && ptr.Campo > 0 é seguro porque ptr.Campo só é acessado quando ptr não é nil.