O que é Func em Go?
A palavra-chave func é o bloco fundamental para definir funções na linguagem Go. Toda lógica executável em Go vive dentro de funções — desde a main() que inicia seu programa até handlers HTTP, goroutines concorrentes e callbacks de processamento. Em Go, funções são cidadãs de primeira classe (first-class citizens), o que significa que podem ser atribuídas a variáveis, passadas como argumentos e retornadas por outras funções.
Diferente de linguagens orientadas a objetos onde métodos estão sempre vinculados a classes, Go trata funções como valores independentes. Isso torna o design de código mais simples e composável, alinhado com a filosofia de simplicidade que permeia toda a linguagem. Combinado com interfaces e structs, funções formam a base da arquitetura de qualquer programa Go.
Uma função em Go é declarada com a palavra-chave func, seguida opcionalmente por um nome, lista de parâmetros entre parênteses, tipos de retorno e o corpo da função entre chaves. Essa sintaxe limpa e consistente é uma das razões pelas quais Go é considerada uma linguagem fácil de aprender.
Declaração básica de funções
A forma mais simples de declarar uma função em Go:
func saudacao() {
fmt.Println("Olá, mundo!")
}
Funções com parâmetros
// Parâmetros com tipos explícitos
func soma(a int, b int) int {
return a + b
}
// Parâmetros do mesmo tipo podem ser agrupados
func multiplica(a, b float64) float64 {
return a * b
}
Go é uma linguagem de tipagem estática, então todo parâmetro precisa de um tipo declarado. Isso evita erros comuns que acontecem em linguagens dinâmicas e facilita a leitura do código por outros desenvolvedores — algo essencial em projetos grandes como microsserviços.
Funções com múltiplos retornos
Uma das características mais distintivas de Go é o suporte nativo a múltiplos valores de retorno:
func dividir(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("divisão por zero")
}
return a / b, nil
}
resultado, err := dividir(10, 3)
if err != nil {
log.Fatal(err)
}
fmt.Println(resultado) // 3.3333...
Esse padrão de retornar (valor, error) é o idioma central de tratamento de erros em Go. Você vai encontrar esse padrão em praticamente toda a biblioteca padrão e em pacotes de terceiros. É tão fundamental que Go oferece suporte especial para ele em diversas construções da linguagem.
Retornos nomeados
Go permite nomear os valores de retorno, o que cria variáveis locais automaticamente inicializadas com o valor zero do tipo:
func retangulo(largura, altura float64) (area, perimetro float64) {
area = largura * altura
perimetro = 2 * (largura + altura)
return // "naked return" — retorna as variáveis nomeadas
}
Retornos nomeados são úteis em funções curtas e como documentação dos valores retornados. Porém, em funções mais longas, o naked return (sem especificar valores) pode prejudicar a legibilidade. A convenção da comunidade Go é usar retornos nomeados com moderação — prefira retornos explícitos em funções com mais de 10 linhas.
Um caso clássico onde retornos nomeados brilham é com defer:
func lerArquivo(caminho string) (conteudo []byte, err error) {
f, err := os.Open(caminho)
if err != nil {
return nil, err
}
defer f.Close()
conteudo, err = io.ReadAll(f)
return // retorna conteudo e err automaticamente
}
Funções variádicas
Funções variádicas aceitam um número indefinido de argumentos do mesmo tipo, usando a sintaxe ...:
func somaTotal(numeros ...int) int {
total := 0
for _, n := range numeros {
total += n
}
return total
}
// Chamadas válidas
somaTotal(1, 2, 3) // 6
somaTotal(1, 2, 3, 4, 5) // 15
somaTotal() // 0
// Passando um slice como argumento variádico
valores := []int{10, 20, 30}
somaTotal(valores...) // 60
Internamente, o parâmetro variádico é recebido como um slice. A função fmt.Println e append() são exemplos clássicos de funções variádicas na biblioteca padrão de Go. A sintaxe ... ao passar um slice expande seus elementos como argumentos individuais.
Funções anônimas e closures
Go suporta funções anônimas — funções sem nome que podem ser declaradas e executadas inline:
// Função anônima executada imediatamente (IIFE)
func() {
fmt.Println("Executando imediatamente")
}()
// Atribuída a uma variável
dobro := func(n int) int {
return n * 2
}
fmt.Println(dobro(5)) // 10
Closures
Uma closure é uma função anônima que captura variáveis do escopo externo. Isso é extremamente poderoso para criar geradores, contadores e callbacks:
func contador() func() int {
count := 0
return func() int {
count++
return count
}
}
c := contador()
fmt.Println(c()) // 1
fmt.Println(c()) // 2
fmt.Println(c()) // 3
A variável count sobrevive entre chamadas porque a closure mantém uma referência a ela. Closures são amplamente usadas em middleware HTTP, handlers de rotas e padrões funcionais.
Closures em goroutines — cuidado!
Um bug extremamente comum ao usar closures com goroutines:
// BUG: todas as goroutines imprimem o mesmo valor
for i := 0; i < 5; i++ {
go func() {
fmt.Println(i) // captura a referência, não o valor
}()
}
// CORRETO: passe como parâmetro
for i := 0; i < 5; i++ {
go func(n int) {
fmt.Println(n) // cada goroutine recebe sua cópia
}(i)
}
A partir do Go 1.22+, o comportamento de variáveis de loop foi alterado para criar uma nova variável por iteração, resolvendo esse gotcha histórico.
Funções como tipos e valores
Em Go, funções têm tipos e podem ser usadas como qualquer outro valor:
// Tipo de função
type Operacao func(int, int) int
func aplicar(a, b int, op Operacao) int {
return op(a, b)
}
soma := func(a, b int) int { return a + b }
sub := func(a, b int) int { return a - b }
fmt.Println(aplicar(10, 5, soma)) // 15
fmt.Println(aplicar(10, 5, sub)) // 5
Esse padrão é a base de estratégias como o strategy pattern e é usado extensivamente em testes (mock de dependências), middleware HTTP e configuração de packages com options pattern.
Métodos vs funções
Em Go, métodos são funções com um receiver — um parâmetro especial que vincula a função a um tipo:
type Circulo struct {
Raio float64
}
// Método com receiver de valor
func (c Circulo) Area() float64 {
return math.Pi * c.Raio * c.Raio
}
// Método com receiver de ponteiro
func (c *Circulo) Redimensionar(fator float64) {
c.Raio *= fator
}
A diferença entre receivers de valor e ponteiro é crucial: receivers de ponteiro podem modificar o struct original e evitam cópia de dados grandes. Use receivers de ponteiro quando o método precisa alterar o estado ou quando o struct é grande.
init() — a função de inicialização
Cada package pode ter uma ou mais funções init() que são executadas automaticamente antes de main():
var config map[string]string
func init() {
config = make(map[string]string)
config["env"] = "production"
fmt.Println("Pacote inicializado")
}
As funções init() são executadas na ordem de importação dos packages. Use-as com moderação — para inicializações simples de estado global, registro de drivers de banco de dados ou validação de configuração.
Boas práticas com funções em Go
- Funções pequenas e focadas — cada função deve fazer uma única coisa bem feita
- Retorne erros, não panic — use o padrão
(valor, error)para tratamento de erros - Prefira parâmetros explícitos a variáveis globais — facilita testes
- Use interfaces para abstrair comportamentos — aceite interfaces, retorne structs concretos
- Evite naked returns em funções longas — prejudica a legibilidade
- Documente funções exportadas — comece o comentário com o nome da função
Para aprofundar seus conhecimentos em funções Go, explore os tutoriais de Go para iniciantes e aprenda como funções se integram com channels em padrões de concorrência.
Perguntas frequentes (FAQ)
Qual a diferença entre func e método em Go?
Uma func é uma função independente, enquanto um método é uma função com um receiver que a vincula a um tipo específico. Métodos permitem que structs tenham comportamento associado, similar a métodos de classe em outras linguagens. Na prática, métodos são funções que recebem um parâmetro extra (o receiver) implicitamente.
Go suporta sobrecarga de funções (overloading)?
Não. Go não suporta sobrecarga de funções — você não pode ter duas funções com o mesmo nome mas parâmetros diferentes no mesmo package. Essa decisão de design mantém o código mais simples e legível. Use nomes descritivos diferentes ou funções variádicas para alcançar flexibilidade similar.
Quando usar retornos nomeados em Go?
Use retornos nomeados em funções curtas (menos de 10 linhas) onde eles servem como documentação adicional, ou quando precisar modificar o valor de retorno em um bloco defer. Evite naked returns em funções longas — retornos explícitos são mais claros e menos propensos a bugs.
Como passar uma função como parâmetro em Go?
Defina o parâmetro com a assinatura da função esperada: func processar(callback func(int) string). Você pode também criar um tipo nomeado com type Handler func(int) string para melhorar a legibilidade. Funções como valores são fundamentais para callbacks, middleware HTTP e padrões de injeção de dependência em Go.