Em Go, funções são cidadãs de primeira classe. Elas podem ser atribuídas a variáveis, passadas como argumentos, retornadas por outras funções e declaradas anonimamente. Ao mesmo tempo, a linguagem impõe uma sintaxe clara e consistente que torna qualquer função Go imediatamente legível, independentemente de quem a escreveu.
Entender funções em profundidade é essencial porque praticamente tudo em Go passa por elas — desde o tratamento de erros até os padrões de concorrência que serão estudados nos módulos seguintes.
Declaração básica
A sintaxe de uma função em Go segue sempre a mesma estrutura: palavra-chave func, nome, parâmetros entre parênteses, tipo de retorno e corpo entre chaves:
package main
import "fmt"
func saudar(nome string) string {
return "Olá, " + nome + "!"
}
func main() {
mensagem := saudar("Ricardo")
fmt.Println(mensagem) // Olá, Ricardo!
}
Quando a função não retorna nada, o tipo de retorno é simplesmente omitido:
func imprimirLinha(texto string) {
fmt.Println("→", texto)
}
Parâmetros do mesmo tipo
Quando múltiplos parâmetros consecutivos têm o mesmo tipo, Go permite declarar o tipo apenas uma vez ao final:
// Forma verbosa
func somar(a int, b int) int {
return a + b
}
// Forma idiomática
func somar(a, b int) int {
return a + b
}
Essa sintaxe se estende a quantos parâmetros forem necessários:
func calcularMedia(a, b, c float64) float64 {
return (a + b + c) / 3
}
Múltiplos valores de retorno
Esta é uma das características mais distintivas do Go. Funções podem retornar múltiplos valores, e isso não é um recurso de nicho — é o mecanismo central pelo qual Go trata erros.
package main
import (
"errors"
"fmt"
)
func dividir(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("divisão por zero não é permitida")
}
return a / b, nil
}
func main() {
resultado, err := dividir(10, 3)
if err != nil {
fmt.Println("Erro:", err)
return
}
fmt.Printf("Resultado: %.4f\n", resultado) // 3.3333
}
O padrão (resultado, error) está presente em praticamente toda função Go que pode falhar. O chamador é obrigado a lidar com ambos os valores — ou descartar explicitamente um deles com _. Esse design torna o tratamento de erros visível e auditável.
Retornos nomeados
Go permite dar nomes aos valores de retorno. Esses nomes funcionam como variáveis locais pré-declaradas e podem ser usados com o return nu — um return sem argumentos que devolve os valores nomeados no estado atual:
func minMax(nums []int) (min, max int) {
min = nums[0]
max = nums[0]
for _, n := range nums[1:] {
if n < min {
min = n
}
if n > max {
max = n
}
}
return // retorna min e max implicitamente
}
func main() {
menor, maior := minMax([]int{3, 1, 7, 2, 9, 4})
fmt.Println(menor, maior) // 1 9
}
Retornos nomeados são úteis quando a função é curta e os nomes adicionam clareza à assinatura. Em funções longas, o return nu pode obscurecer o que está sendo retornado — nesses casos, prefira o retorno explícito.
Funções variádicas
Uma função variádica aceita um número indefinido de argumentos do mesmo tipo. O último parâmetro recebe o prefixo ... e é tratado internamente como um slice:
func somarTodos(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
func main() {
fmt.Println(somarTodos(1, 2, 3)) // 6
fmt.Println(somarTodos(10, 20, 30, 40)) // 100
fmt.Println(somarTodos()) // 0
}
Quando você já tem um slice e quer passá-lo para uma função variádica, use o operador ... na chamada para expandir o slice:
numeros := []int{5, 10, 15, 20}
fmt.Println(somarTodos(numeros...)) // 50
A função fmt.Println é ela própria variádica — aceita qualquer número de argumentos de qualquer tipo, graças ao uso de ...interface{} (ou ...any na notação moderna).
Funções como valores
Em Go, funções têm tipo assim como int ou string têm. Uma variável pode armazenar uma função, e funções podem ser passadas como argumentos ou retornadas por outras funções:
package main
import "fmt"
func aplicar(nums []int, fn func(int) int) []int {
resultado := make([]int, len(nums))
for i, n := range nums {
resultado[i] = fn(n)
}
return resultado
}
func dobrar(n int) int { return n * 2 }
func quadrado(n int) int { return n * n }
func main() {
nums := []int{1, 2, 3, 4, 5}
fmt.Println(aplicar(nums, dobrar)) // [2 4 6 8 10]
fmt.Println(aplicar(nums, quadrado)) // [1 4 9 16 25]
}
O tipo func(int) int descreve uma função que recebe um int e retorna um int. Esse tipo pode ser nomeado com type para tornar o código mais legível:
type Transformador func(int) int
func aplicar(nums []int, fn Transformador) []int {
// ...
}
Funções anônimas e closures
Funções anônimas são funções sem nome, declaradas diretamente onde são usadas. Quando uma função anônima referencia variáveis do escopo externo, ela forma uma closure — capturando essas variáveis e mantendo acesso a elas mesmo após o escopo original ter encerrado:
package main
import "fmt"
func contadorFabrica() func() int {
count := 0
return func() int {
count++
return count
}
}
func main() {
contador := contadorFabrica()
fmt.Println(contador()) // 1
fmt.Println(contador()) // 2
fmt.Println(contador()) // 3
outroContador := contadorFabrica()
fmt.Println(outroContador()) // 1 — estado independente
}
A variável count é capturada pelo closure. Cada chamada a contadorFabrica cria um novo count independente. Esse padrão é muito utilizado para criar geradores, middlewares e funções com estado encapsulado.
Funções anônimas executadas imediatamente
Uma função anônima pode ser declarada e chamada na mesma expressão. Esse padrão é chamado de IIFE — Immediately Invoked Function Expression:
resultado := func(a, b int) int {
return a + b
}(10, 20)
fmt.Println(resultado) // 30
Em Go, IIFEs são usadas com frequência junto ao defer e a goroutines para isolar escopo ou capturar variáveis em seu valor atual dentro de laços.
Armadilha clássica: closure em laço
Um erro comum ao usar closures dentro de laços é capturar a variável do laço por referência em vez do valor atual:
// Código com problema
funcs := make([]func(), 3)
for i := 0; i < 3; i++ {
funcs[i] = func() {
fmt.Println(i) // captura a variável i, não o valor
}
}
for _, f := range funcs {
f()
}
// Imprime: 3 3 3 — não 0 1 2
A correção é criar uma cópia local da variável dentro do laço:
for i := 0; i < 3; i++ {
i := i // nova variável i local a cada iteração
funcs[i] = func() {
fmt.Println(i)
}
}
// Imprime: 0 1 2
Funções init
Go permite declarar funções especiais chamadas init em qualquer arquivo de um pacote. Elas são executadas automaticamente antes da função main, sem precisar ser chamadas explicitamente. São usadas para inicializar estado de pacote, registrar drivers ou validar configurações:
package main
import "fmt"
var configuracao string
func init() {
configuracao = "produção"
fmt.Println("init executado")
}
func main() {
fmt.Println("main executado")
fmt.Println("Ambiente:", configuracao)
}
// Saída:
// init executado
// main executado
// Ambiente: produção
Um arquivo pode ter múltiplas funções init, e pacotes importados têm seus init executados antes do pacote que os importa. O uso deve ser moderado — lógica complexa em init dificulta testes e rastreamento de bugs.
Resumo do que foi coberto
Este artigo apresentou funções em Go de forma abrangente: declaração básica, agrupamento de parâmetros, múltiplos retornos com o padrão de erro, retornos nomeados, funções variádicas, funções como valores, closures e sua armadilha em laços, IIFEs e funções init. Esses recursos formam a base de toda a expressividade do Go.
Referências e leituras complementares
-
Especificação da linguagem Go — Function types — Definição formal do tipo função. https://go.dev/ref/spec#Function_types
-
A Tour of Go — Functions — Seção interativa com exemplos de múltiplos retornos e closures. https://go.dev/tour/basics/4
-
Go by Example: Functions — Exemplos práticos de funções em Go. https://gobyexample.com/functions
-
Go by Example: Closures — Exemplos de closures e fábricas de funções. https://gobyexample.com/closures
-
Go by Example: Variadic Functions — Exemplos de funções com número variável de argumentos. https://gobyexample.com/variadic-functions
-
Effective Go — Functions — Boas práticas e idiomas recomendados para funções. https://go.dev/doc/effective_go#functions