Go

Estruturas de Controle em Go: if, for, switch e defer na Prática

13 min de leitura

Estruturas de Controle em Go: if, for, switch e defer na Prática

Estruturas de Controle em Go: if, for, switch e defer Go é uma linguagem que prioriza simplicidade e clareza. Suas estruturas de controle refletem essa filosofia: são diretas, sem sintaxe desnecessária e com comportamentos bem definidos. Neste artigo, você aprenderá a dominar os quatro pilares do controle de fluxo em Go de forma prática e fundamentada. Cada estrutura tem seu propósito específico, e entendê-las profundamente é essencial para escrever código Go idiomático e eficiente. A abordagem aqui é progressiva: começamos com as decisões simples (if), passamos por iterações (for), depois ramificações múltiplas (switch) e finalizamos com um conceito único de Go (defer). Você não apenas aprenderá a sintaxe, mas compreenderá o "porquê" por trás de cada decisão de design. Condicional if: Decisões Simples e Compostas O if em Go é tão minimalista quanto parece. Não há parênteses obrigatórios ao redor da condição, mas as chaves são obrigatórias, mesmo que o bloco tenha uma única linha. Isso força uma consistência visual

<h2>Estruturas de Controle em Go: if, for, switch e defer</h2>

<p>Go é uma linguagem que prioriza simplicidade e clareza. Suas estruturas de controle refletem essa filosofia: são diretas, sem sintaxe desnecessária e com comportamentos bem definidos. Neste artigo, você aprenderá a dominar os quatro pilares do controle de fluxo em Go de forma prática e fundamentada. Cada estrutura tem seu propósito específico, e entendê-las profundamente é essencial para escrever código Go idiomático e eficiente.</p>

<p>A abordagem aqui é progressiva: começamos com as decisões simples (if), passamos por iterações (for), depois ramificações múltiplas (switch) e finalizamos com um conceito único de Go (defer). Você não apenas aprenderá a sintaxe, mas compreenderá o &quot;porquê&quot; por trás de cada decisão de design.</p>

<h2>Condicional if: Decisões Simples e Compostas</h2>

<p>O if em Go é tão minimalista quanto parece. Não há parênteses obrigatórios ao redor da condição, mas as chaves são <strong>obrigatórias</strong>, mesmo que o bloco tenha uma única linha. Isso força uma consistência visual no código que Go cultiva deliberadamente.</p>

<p>A forma básica é direta: você avalia uma expressão booleana e executa um bloco caso seja verdadeira. Mas há nuances importantes que vão além do óbvio.</p>

<pre><code class="language-go">package main

import &quot;fmt&quot;

func main() {

idade := 25

// if simples

if idade &gt;= 18 {

fmt.Println(&quot;Você é maior de idade&quot;)

}

// if com else

if idade &lt; 13 {

fmt.Println(&quot;Criança&quot;)

} else if idade &lt; 18 {

fmt.Println(&quot;Adolescente&quot;)

} else {

fmt.Println(&quot;Adulto&quot;)

}

}</code></pre>

<p>Um aspecto poderoso do if em Go é a possibilidade de declarar e avaliar uma variável na mesma linha. Isso é particularmente útil ao trabalhar com funções que retornam um valor e um erro. A variável declarada nesse contexto é escopo-limitada ao bloco if (e seus else), o que reduz a poluição de variáveis globais.</p>

<pre><code class="language-go">package main

import (

&quot;fmt&quot;

&quot;strconv&quot;

)

func main() {

numero := &quot;42&quot;

// Declarar e usar uma variável no if

if valor, err := strconv.Atoi(numero); err == nil {

fmt.Printf(&quot;Número convertido com sucesso: %d\n&quot;, valor)

} else {

fmt.Printf(&quot;Erro na conversão: %v\n&quot;, err)

}

// valor não existe aqui — escopo limitado ao if

}</code></pre>

<p>Isso é mais do que conveniência sintática: é uma filosofia de Go sobre manter variáveis próximas ao seu ponto de uso e evitar estado global desnecessário.</p>

<h2>Iteração for: O Único Loop de Go</h2>

<p>Ao contrário de linguagens como Python ou Java, Go possui apenas uma palavra-chave para iteração: <strong>for</strong>. Não há while, do-while ou foreach separados. Tudo é for, mas com múltiplas formas que cobrem todos os casos de uso.</p>

<p>A primeira forma é a clássica, com inicialização, condição e incremento. A inicialização é opcional, a condição determina quando parar, e o incremento (ou qualquer comando) executa após cada iteração.</p>

<pre><code class="language-go">package main

import &quot;fmt&quot;

func main() {

// for clássico

for i := 0; i &lt; 5; i++ {

fmt.Printf(&quot;Iteração %d\n&quot;, i)

}

// for como while (apenas condição)

contador := 0

for contador &lt; 3 {

fmt.Printf(&quot;Contador: %d\n&quot;, contador)

contador++

}

// for infinito

// for {

// fmt.Println(&quot;Executa para sempre até break&quot;)

// break

// }

}</code></pre>

<p>A segunda forma crucial é o <strong>range</strong>, que itera sobre coleções. Com range, você obtém o índice e o valor (ou apenas um deles). Isso é extremamente útil e elimina classes inteiras de bugs relacionados a gerenciamento manual de índices.</p>

<pre><code class="language-go">package main

import &quot;fmt&quot;

func main() {

nomes := []string{&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;}

// índice e valor

for i, nome := range nomes {

fmt.Printf(&quot;%d: %s\n&quot;, i, nome)

}

// apenas valor (descarta índice com _)

for _, nome := range nomes {

fmt.Printf(&quot;Nome: %s\n&quot;, nome)

}

// apenas índice

for i := range nomes {

fmt.Printf(&quot;Índice: %d\n&quot;, i)

}

// iterando sobre mapa

mapa := map[string]int{&quot;x&quot;: 10, &quot;y&quot;: 20}

for chave, valor := range mapa {

fmt.Printf(&quot;%s: %d\n&quot;, chave, valor)

}

}</code></pre>

<p>Um detalhe crítico: ao iterar sobre um mapa com range, a ordem <strong>não é garantida</strong>. Isso é uma decisão deliberada de Go para evitar que código dependa de uma ordem que não está documentada. Se você precisa de ordem, deve ordenar explicitamente antes de iterar.</p>

<h2>Switch: Ramificação Limpa para Múltiplas Condições</h2>

<p>O switch em Go é elegante e poderoso. Diferentemente de muitas linguagens, Go <strong>não</strong> requer break entre cases — a execução automaticamente &quot;sai&quot; após um case correspondente, a menos que você use <strong>fallthrough</strong> explicitamente.</p>

<p>Isso elimina uma das fontes mais comuns de bugs em linguagens como C ou JavaScript, onde esquecer o break causa comportamento inesperado.</p>

<pre><code class="language-go">package main

import &quot;fmt&quot;

func main() {

dia := 3

// switch básico

switch dia {

case 1:

fmt.Println(&quot;Segunda-feira&quot;)

case 2:

fmt.Println(&quot;Terça-feira&quot;)

case 3:

fmt.Println(&quot;Quarta-feira&quot;)

default:

fmt.Println(&quot;Dia inválido&quot;)

}

}</code></pre>

<p>Você pode ter múltiplos valores em um único case, separados por vírgulas. Isso reduz duplicação quando vários casos devem executar o mesmo código.</p>

<pre><code class="language-go">package main

import &quot;fmt&quot;

func main() {

caractere := &#039;A&#039;

switch caractere {

case &#039;a&#039;, &#039;e&#039;, &#039;i&#039;, &#039;o&#039;, &#039;u&#039;, &#039;A&#039;, &#039;E&#039;, &#039;I&#039;, &#039;O&#039;, &#039;U&#039;:

fmt.Println(&quot;Vogal&quot;)

default:

fmt.Println(&quot;Consoante ou não-letra&quot;)

}

}</code></pre>

<p>Go também permite switch sem uma expressão inicial, onde cada case é uma condição booleana completa. Isso é essencialmente syntactic sugar para uma série de if-else if, mas é frequentemente mais legível.</p>

<pre><code class="language-go">package main

import &quot;fmt&quot;

func main() {

idade := 25

switch {

case idade &lt; 13:

fmt.Println(&quot;Criança&quot;)

case idade &lt; 18:

fmt.Println(&quot;Adolescente&quot;)

case idade &lt; 65:

fmt.Println(&quot;Adulto&quot;)

default:

fmt.Println(&quot;Idoso&quot;)

}

}</code></pre>

<p>O fallthrough é explícito e raro. Quando você o usa, fica claro na leitura do código que a intenção é continuar para o próximo case — não é um acidente, é uma decisão documentada.</p>

<pre><code class="language-go">package main

import &quot;fmt&quot;

func main() {

numero := 2

switch numero {

case 1:

fmt.Println(&quot;Um&quot;)

fallthrough

case 2:

fmt.Println(&quot;Um ou Dois&quot;)

fallthrough

case 3:

fmt.Println(&quot;Um, Dois ou Três&quot;)

default:

fmt.Println(&quot;Outro&quot;)

}

// Saída: Um ou Dois / Um, Dois ou Três

}</code></pre>

<h2>Defer: Adiando Execução com Garantia</h2>

<p>O defer é um recurso único de Go que não existe em muitas outras linguagens. Ele permite que você agende uma função para ser executada após a função atual retornar. Parece simples, mas suas aplicações são profundas.</p>

<p>A principal utilidade do defer é garantir que certos códigos de limpeza sejam <strong>sempre</strong> executados, mesmo que exceções ocorram (ou, em Go, mesmo que você retorne prematuramente). Isso é particularmente valioso ao trabalhar com arquivos, conexões de banco de dados ou locks.</p>

<pre><code class="language-go">package main

import (

&quot;fmt&quot;

&quot;os&quot;

)

func main() {

arquivo, err := os.Create(&quot;teste.txt&quot;)

if err != nil {

fmt.Println(&quot;Erro ao criar arquivo:&quot;, err)

return

}

// defer garante que Close será chamado

defer arquivo.Close()

arquivo.WriteString(&quot;Olá, Go!\n&quot;)

fmt.Println(&quot;Arquivo escrito com sucesso&quot;)

// arquivo.Close() é chamado automaticamente ao sair da função

}</code></pre>

<p>Um comportamento crucial a entender: o defer adiam a <strong>execução</strong>, mas os <strong>argumentos são avaliados imediatamente</strong>. Se você passar o valor de uma variável, aquele valor é &quot;congelado&quot; no momento do defer.</p>

<pre><code class="language-go">package main

import &quot;fmt&quot;

func main() {

contador := 1

defer fmt.Println(&quot;Defer 1:&quot;, contador) // valor 1 é &quot;congelado&quot;

contador = 2

defer fmt.Println(&quot;Defer 2:&quot;, contador) // valor 2 é &quot;congelado&quot;

contador = 3

fmt.Println(&quot;Durante execução:&quot;, contador)

// Saída:

// Durante execução: 3

// Defer 2: 2

// Defer 1: 1

}</code></pre>

<p>Note que defers são executados em ordem LIFO (Last In, First Out) — o último defer registrado é o primeiro a executar. Isso é intencional: funciona como uma pilha, permitindo que você estabeleça dependências de limpeza na ordem correta.</p>

<pre><code class="language-go">package main

import (

&quot;fmt&quot;

&quot;sync&quot;

)

func main() {

var mutex sync.Mutex

// Adquire lock

mutex.Lock()

defer mutex.Unlock() // Garante que unlock sempre ocorrerá

fmt.Println(&quot;Seção crítica protegida&quot;)

// Mesmo que você retorne aqui, Unlock será chamado

// Mesmo que panic ocorra, Unlock será chamado

}</code></pre>

<p>Uma aplicação avançada é usar defer com funções anônimas que capturam variáveis por referência, permitindo lógica de limpeza mais sofisticada.</p>

<pre><code class="language-go">package main

import &quot;fmt&quot;

func main() {

valor := &quot;inicial&quot;

defer func() {

fmt.Println(&quot;Limpeza final, valor é:&quot;, valor)

}()

valor = &quot;modificado&quot;

// Saída: Limpeza final, valor é: modificado

}</code></pre>

<h2>Combinando Estruturas: Padrões Práticos</h2>

<p>Na prática, você raramente usa essas estruturas isoladamente. Elas trabalham juntas para resolver problemas reais. Um padrão comum é combinar for com if para filtrar dados, ou usar switch dentro de um for para diferentes casos.</p>

<pre><code class="language-go">package main

import &quot;fmt&quot;

func main() {

numeros := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

fmt.Println(&quot;Números pares maiores que 4:&quot;)

for _, numero := range numeros {

if numero &gt; 4 &amp;&amp; numero%2 == 0 {

fmt.Println(numero)

}

}

}</code></pre>

<p>Outro padrão essencial é usar defer para garantir limpeza dentro de loops ou funções complexas que fazem múltiplas alocações.</p>

<pre><code class="language-go">package main

import (

&quot;fmt&quot;

&quot;os&quot;

)

func processarArquivos(nomes []string) {

for _, nome := range nomes {

arquivo, err := os.Open(nome)

if err != nil {

fmt.Printf(&quot;Erro ao abrir %s: %v\n&quot;, nome, err)

continue

}

defer arquivo.Close() // Garante fechamento mesmo com continue

// processa arquivo

fmt.Printf(&quot;Processando %s\n&quot;, nome)

}

}

func main() {

processarArquivos([]string{&quot;arquivo1.txt&quot;, &quot;arquivo2.txt&quot;})

}</code></pre>

<p>Entender quando cada estrutura é apropriada é a marca de um programador Go competente. Use if para lógica simples, for para iterações, switch para múltiplas ramificações sobre um valor específico, e defer para garantir limpeza. Essa combinação cobre praticamente todos os cenários de controle de fluxo que você encontrará.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://go.dev/tour/flowcontrol" target="_blank" rel="noopener noreferrer">A Tour of Go - Flow Control Statements</a></li>

<li><a href="https://go.dev/doc/effective_go#control-structures" target="_blank" rel="noopener noreferrer">Effective Go - Control structures</a></li>

<li><a href="https://www.gopl.io/" target="_blank" rel="noopener noreferrer">The Go Programming Language (Donovan &amp; Kernighan)</a> — Capítulos 1-5 cobrem estruturas de controle em profundidade</li>

<li><a href="https://github.com/golang/go/wiki/CodeReviewComments#defer" target="_blank" rel="noopener noreferrer">Go Code Review Comments - defer</a></li>

<li><a href="https://go.dev/blog/defer-panic-and-recover" target="_blank" rel="noopener noreferrer">Official Go Blog - Defer, Panic, and Recover</a></li>

</ul>

<p>&lt;!-- FIM --&gt;</p>

Comentários

Mais em Go

Middleware de Autenticação, Logging e Rate Limiting em Go na Prática
Middleware de Autenticação, Logging e Rate Limiting em Go na Prática

Introdução: Por que Middleware Importa em Go Middleware é um padrão fundament...

Boas Práticas de sync.WaitGroup e sync.Once em Go: Coordenação de Goroutines para Times Ágeis
Boas Práticas de sync.WaitGroup e sync.Once em Go: Coordenação de Goroutines para Times Ágeis

Entendendo Goroutines e a Necessidade de Sincronização Go foi projetado com c...

Introdução ao Go: Filosofia, Instalação, Toolchain e Primeiro Programa na Prática
Introdução ao Go: Filosofia, Instalação, Toolchain e Primeiro Programa na Prática

Por que Go? A Filosofia por Trás da Linguagem Go (ou Golang) foi criada em 20...