Métodos: Receivers de Valor vs. Ponteiro
Métodos anexam comportamento a tipos através de receivers - uma cópia (T) ou um ponteiro (*T).
Busque em todas as páginas da documentação
Métodos anexam comportamento a tipos através de receivers - uma cópia (T) ou um ponteiro (*T).
A escolha afeta a mutabilidade, alocações, satisfação de interface e consistência da API em seu pacote.
Um receiver de valor copia a struct para a chamada do método.
Mutações dentro do método não afetam a cópia do chamador.
Um receiver de ponteiro compartilha o valor subjacente.
Mutações persistem e evitam a cópia de structs grandes.
Conjuntos de métodos determinam a satisfação de interface: o tipo de valor T inclui apenas métodos com receivers de valor; o tipo de ponteiro *T inclui métodos com receivers de valor e ponteiro.
Escolha um estilo de receiver por tipo, a menos que haja uma exceção documentada.
Use receivers de ponteiro quando os métodos modificam o estado ou quando o tamanho da struct torna a cópia custosa.
Cartão de receita de referência rápida - pronto para copiar e colar.
type Buffer struct {
b []byte
}
// Receiver de ponteiro: muta e corresponde a APIs no estilo io.Writer.
func (buf *Buffer) Write(p []byte) (int, error) {
buf.b = append(buf.b, p...)
return len(p), nil
}
// Receiver de valor: snapshot somente leitura.
func (buf Buffer) Bytes() []byte {
return append([]byte(nil), buf.b...)
}
// Consistência: se algum método precisar de *T, prefira *T para todos os métodos.
func (buf *Buffer) Reset() { buf.b = buf.b[:0] }Quando usar isso:
Inc, Write, SetState).fmt.Stringer em structs grandes está bom de qualquer forma; sync.Mutex não deve ser copiado).time.Time usa métodos de valor).T e *T.package ledger
import "fmt"
type Account struct {
id string
balance int64
}
func NewAccount(id string) *Account {
return &Account{id: id}
}
func (a *Account) Credit(cents int64) error {
if cents < 0 {
return fmt.Errorf("ledger: negative credit %d", cents)
}
a.balance += cents
return nil
}
func (a Account) Balance() int64 {
return a.balance
}
type Creditor interface {
Credit(cents int64) error
}
func Process(c Creditor, amount int64) error {
return c.Credit(amount)
}
func Example() error {
acct := NewAccount("user-1")
if err := Process(acct, 500); err != nil {
return err
}
return nil
}O que isso demonstra:
Credit modifica o saldo.Balance retorna um snapshot sem expor estado mutável.Creditor exige Credit; satisfeita por *Account, não por valor Account.*Account para que os chamadores cheguem ao conjunto de métodos de ponteiro.a.Credit(x) se torna Credit(a, x) com o receiver como primeiro argumento.T: métodos com receiver T*T: métodos com receiver T e *T| Sinal | Preferir |
|---|---|
| Modifica campos do receiver | *T |
Struct contém sync.Mutex ou campos semelhantes que não devem ser copiados | Apenas *T |
| Pequeno tipo de valor imutável | T |
| Struct grande (mais que alguns ponteiros) | *T para desempenho |
| Receivers mistos no mesmo tipo | Evitar - escolher *T se houver alguma mutação |
| Receiver do método | var x T satisfaz? | var p *T satisfaz? |
|---|---|---|
func (T) M() | Sim | Sim |
func (*T) M() | Não | Sim |
type S struct{ n int }
func (s S) V() int { return s.n }
func (s *S) P() int { s.n++; return s.n }
var i interface {
V() int
P() int
}
// i = S{} // erro de compilação: S não tem P
i = &S{} // OKDocumente construtores que retornam *T quando métodos de ponteiro existem.
*T ou retornar explicitamente uma cópia atualizada.sync.Mutex e causam pânico. Correção: apenas receivers de ponteiro; às vezes, exportar a struct.func (T) Foo e func (*T) Bar quebram atribuições de interface T. Correção: unificar em *T quando qualquer método de ponteiro existir.var p *T; p.Method() se o método lidar com nulo (veja guardas nil na biblioteca padrão). Correção: documentar ou entrar em pânico cedo para nulos inválidos.Account a Creditor falha. Correção: armazenar *Account ou adicionar wrappers de receiver de valor apenas quando semanticamente correto.*bytes.Buffer.| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
Funções de pacote func F(t *T) | Nenhum conjunto de métodos necessário | Implementar interfaces padrão |
Padrão de retorno-novo imutável func (t T) WithX() T | Pequenos tipos de valor como configuração | Structs grandes ou caminhos de alta frequência |
| Embutir interfaces | Compor comportamento | Ocultar regras de receiver atrás de tipos opacos |
| Genéricos em funções | Algoritmos sobre tipos | Necessidade de despacho de método virtual |
O conjunto de métodos anexados a um tipo usado para satisfação de interface.
T e *T têm conjuntos diferentes quando existem métodos apenas de ponteiro.
Muitas equipes usam *T por padrão para structs para deixar espaço para mutação e evitar surpresas de cópia.
Receivers de valor permanecem corretos para tipos imutáveis minúsculos.
Ergonomia: v.Method() funciona quando v é endereçável e o método tem um receiver de ponteiro.
Valores não endereçáveis (elementos de mapa, resultados de função) podem não se qualificar.
Sim - se todos os métodos usarem T, tanto valores T quanto *T satisfazem a interface.
Métodos apenas de ponteiro restringem a satisfação a *T.
Receivers não alteram as regras de comparação de structs.
Structs comparáveis ainda comparam campo a campo; slices/maps internos tornam a struct não comparável.
Eles expõem mutação compartilhada - isso é intencional.
A imutabilidade é imposta por disciplina ou retornando cópias, não apenas por receivers de valor.
Defina tipos nomeados: type Celsius float64 então func (c Celsius) F() Fahrenheit.
Não é possível anexar métodos diretamente a int embutido.
Receivers de valor de structs grandes copiam a cada chamada.
Faça o profiling antes de micro-otimizar; receivers de ponteiro geralmente vencem para structs grandes.
Padrão comum: func (s *Server) ServeHTTP(...) compartilhando dependências em Server.
Handlers pequenos e sem estado podem ser funções simples.
Reconciliadores são structs com receivers de ponteiro que modificam o estado e chamam client.Client.
Siga as convenções de scaffold geradas para conformidade de interface.
Possível, mas confuso para design de interface.
Prefira estilo de receiver exportado consistente.
Ferramentas como staticcheck sinalizam mutexes copiados e nomes de receiver inconsistentes.
Execute golangci-lint em CI.
Versões de Stack: Esta página foi escrita para Go 1.26.x (padrão GC Green Tea, go fix modernizers - verifique o patch na compilação), chi (última - verifique na compilação), gin (última - verifique na compilação), echo (última - verifique na compilação), google.golang.org/grpc (última - verifique na compilação), sigs.k8s.io/controller-runtime (última - verifique na compilação), kubebuilder (última - verifique na compilação), tinygo (última - verifique os alvos de placa na compilação), wazero (última - verifique na compilação) e golangci-lint (última - verifique o conjunto de linters na compilação).
Revisado por Chris St. John·Última atualização: 18 de jul. de 2026