Da trilha de Go Este conceito ainda não saiu como card do dia. ir para o card de hoje

Go Funções Intermediário

Closures

Em Go, uma closure é uma função que captura variáveis do escopo onde foi definida. Isso permite criar funções que "lembram" do estado entre chamadas, gerar sequências, criar middlewares e muito mais.

A captura é feita por referência: a closure acessa a variável original, não uma cópia. Isso significa que se a variável mudar depois, a closure verá o novo valor. Esse comportamento é poderoso, mas exige cuidado com concorrência e com loops.

No Go 1.22, a semântica de variáveis de loop mudou: cada iteração cria uma nova variável, então closures criadas dentro do loop capturam valores distintos. Antes, todas as closures compartilhavam a mesma variável, o que causava bugs clássicos.

Closures são usadas em geradores (funções que retornam a próxima função), middlewares (funções que envolvem outras) e para encapsular estado sem criar structs. Entender closures é essencial para escrever código Go idiomático e seguro.

Pontos-chave

  • Closures capturam variáveis por referência, não por valor.
  • Geradores usam closures para manter estado entre chamadas.
  • No Go 1.22+, cada iteração de loop tem sua própria variável, evitando o bug de captura compartilhada.
  • Middlewares são closures que recebem e retornam funções, permitindo encadear comportamentos.
  • Cuidado com concorrência: closures que capturam variáveis mutáveis podem causar data races.
closures.go
package main

import "fmt"

// contador retorna uma closure que incrementa e retorna um valor.
func contador() func() int {
	i := 0
	return func() int {
		i++
		return i
	}
}

// gerador retorna uma closure que gera números pares até um limite.
func geradorPares(limite int) func() (int, bool) {
	n := 0
	return func() (int, bool) {
		if n > limite {
			return 0, false
		}
		valor := n
		n += 2
		return valor, true
	}
}

func main() {
	// Closure contadora
	c := contador()
	fmt.Println(c()) // 1
	fmt.Println(c()) // 2
	fmt.Println(c()) // 3

	// Gerador de pares
	prox := geradorPares(6)
	for {
		v, ok := prox()
		if !ok {
			break
		}
		fmt.Println(v)
	}

	// Loop var no Go 1.22: cada iteração tem sua própria variável
	funcs := make([]func(), 3)
	for i := 0; i < 3; i++ {
		funcs[i] = func() { fmt.Println(i) }
	}
	for _, f := range funcs {
		f()
	}
}

Exercícios

  1. 1
    Fácil

    Contador com closure

    Crie uma função novoContador que retorna uma closure. A closure deve incrementar um contador interno e retornar o valor atual a cada chamada. No main, crie um contador, chame-o três vezes e imprima os valores.

    Resolver
  2. 2
    Médio

    Gerador de Fibonacci

    Implemente uma função geradorFibonacci que retorna uma closure. A closure deve gerar o próximo número da sequência de Fibonacci a cada chamada, começando em 0 e 1. No main, imprima os 10 primeiros números.

    Resolver
  3. 3
    Desafio

    Middleware com closures

    Crie um sistema de middlewares usando closures. Defina um tipo Handler que é func(string) string. Crie uma função aplicarMiddlewares que recebe um Handler e uma lista de middlewares (cada um é func(Handler) Handler) e retorna o Handler final. Implemente dois middlewares: comPrefixo que adiciona ">> " ao início da resposta, e comMaiusculas que converte a resposta para maiúsculas. No main, aplique ambos a um handler que retorna "olá, " + nome e teste com "mundo".

    Resolver

Continue estudando