Parênteses estranhos antes do nome da função em Go: análise completa
Imagine: você abre um código Go de outra pessoa e vê algo que faz seu olho tremer:
func (u User) Greet() string { return "Olá, " + u.Name}Pare. O que é esse (u User) antes do nome da função? Em Python, para métodos, usamos def greet(self):, em Java — public String greet() dentro de uma classe. E aqui — uns parênteses, ainda mais com um tipo dentro. Loucura? Não, genial!
Essa construção é uma das funcionalidades mais poderosas do Go, que frequentemente confunde iniciantes. Na verdade, isso é um receiver (receptor) — a chave para entender a programação orientada a objetos em Go.
Neste artigo, vamos olhar por baixo do capô do compilador Go, analisar como esses parênteses funcionam e por que sem eles seu código parecerá dos anos 90. Pronto? Vamos lá!
O que é um receiver e para que serve?
Em Go, não existem classes no sentido clássico. Mas existem structs (estruturas) e métodos. Os parênteses antes do nome da função são exatamente a declaração de um método para um tipo específico. Tecnicamente, isso é chamado de receiver parameter.
A sintaxe é simples:
func (receiver_name ReceiverType) MethodName(params) return_type { // corpo do método}Aqui, receiver_name é uma variável através da qual você acessa os campos da struct (como self em Python ou this em Java). E ReceiverType é o seu tipo (geralmente uma struct).
Exemplo da vida real:
package main
import "fmt"
type User struct { Name string Age int}
// Método com receiverfunc (u User) SayHello() { fmt.Printf("Olá! Eu sou %s, tenho %d anos.", u.Name, u.Age)}
func main() { user := User{Name: "Alice", Age: 25} user.SayHello() // Saída: Olá! Eu sou Alice, tenho 25 anos.}Observe: chamamos o método usando um ponto, como em qualquer linguagem OOP. Mas por baixo dos panos, o Go faz um truque esperto.
Value receiver vs Pointer receiver: batalha dos titãs
Em Go, existem dois tipos de receivers: por valor (value receiver) e por ponteiro (pointer receiver). A diferença é colossal, e não entender isso é uma causa frequente de bugs.
Value receiver
Quando você escreve func (u User) ..., o Go copia toda a struct. O método trabalha com uma cópia do objeto. Alterações dentro do método não afetarão o original.
func (u User) Birthday() { u.Age++ // Isso alterará apenas a cópia!}
func main() { user := User{Name: "Bob", Age: 30} user.Birthday() fmt.Println(user.Age) // Ainda 30! Bug?}Esse código não gerará erro de compilação, mas é logicamente incorreto. Você espera que a idade aumente, mas ela permanece a mesma. Bem-vindo ao mundo do value receiver!
Pointer receiver
Se você usar um ponteiro: func (u *User) ..., o método recebe uma referência ao objeto original. Agora, as alterações serão visíveis para todos.
func (u *User) Birthday() { u.Age++ // Alteramos o original}
func main() { user := &User{Name: "Bob", Age: 30} // Criamos um ponteiro user.Birthday() fmt.Println(user.Age) // 31! Funciona!}Regra de ouro: se o método altera o estado da struct ou a struct é grande (para evitar cópia), use pointer receiver. Se o método apenas lê dados, use value receiver.
Como isso funciona por baixo dos panos?
Agora, a parte mais interessante. Quando o compilador Go vê:
type Rectangle struct { Width, Height float64}
func (r Rectangle) Area() float64 { return r.Width * r.Height}Ele transforma isso em uma função comum, onde o receiver se torna o primeiro argumento:
Blogs
Book Recommendations
Leia também
Docker e PHP: conteinerização de aplicações para desenvolvedores
Docker e Rust: conteinerização de aplicações para produção
Tratamento de erros em Go: try/catch e técnicas avançadas
Programação Funcional em Java: Técnicas Avançadas e Stream API
Como o go test executa todos os testes do projeto: análise por baixo dos panos