Não está claro para mim em que caso eu gostaria de usar um receptor de valor em vez de sempre usar um receptor de ponteiro.
Para recapitular os documentos:
type T struct {
a int
}
func (tv T) Mv(a int) int { return 0 } // value receiver
func (tp *T) Mp(f float32) float32 { return 1 } // pointer receiver
A documentação também diz "Para tipos como tipos básicos, fatias e pequenas estruturas, um receptor de valor é muito barato, portanto, a menos que a semântica do método exija um ponteiro, um receptor de valor é eficiente e claro."
Primeiro ponto ele diz que é "muito barato", mas a questão é mais se é mais barato do que o receptor de ponteiro. Então fiz um pequeno benchmark (code on gist) que me mostrou que o receptor de ponteiro é mais rápido mesmo para uma estrutura que tem apenas um campo de string. Estes são os resultados:
// Struct one empty string property
BenchmarkChangePointerReceiver 2000000000 0.36 ns/op
BenchmarkChangeItValueReceiver 500000000 3.62 ns/op
// Struct one zero int property
BenchmarkChangePointerReceiver 2000000000 0.36 ns/op
BenchmarkChangeItValueReceiver 2000000000 0.36 ns/op
(Editar: observe que o segundo ponto se tornou inválido nas versões mais recentes de go, consulte os comentários) .
Segundo ponto , é "eficiente e claro", o que é mais uma questão de gosto, não é? Pessoalmente, prefiro consistência usando todos os lugares da mesma maneira. Eficiência em que sentido? Em termos de desempenho, parece que os ponteiros são quase sempre mais eficientes. Poucos testes com uma propriedade int mostraram vantagem mínima do receptor Value (intervalo de 0,01-0,1 ns / op)
Alguém pode me dizer um caso em que um receptor de valor claramente faz mais sentido do que um receptor de ponteiro? Ou estou fazendo algo errado no benchmark, esqueci outros fatores?
Respostas:
Observe que o FAQ menciona consistência
Conforme mencionado neste tópico :
Agora:
O comentário da revisão do código pode ajudar:
A parte em negrito é encontrada, por exemplo, em
net/http/server.go#Write()
:fonte
The rule about pointers vs. values for receivers is that value methods can be invoked on pointers and values, but pointer methods can only be invoked on pointers
Não é verdade, na verdade. Ambos os métodos de receptor de valor e receptor de ponteiro podem ser chamados em um ponteiro ou não-ponteiro digitado corretamente. Independentemente de como o método é chamado, dentro do corpo do método, o identificador do receptor se refere a um valor por cópia quando um receptor de valor é usado e um ponteiro quando um receptor de ponteiro é usado: Veja play.golang.org/p / 3WHGaAbURMInt(5).increment_by_one_ptr()
. Da mesma forma, uma característica que define o métodoincrement_by_one_ptr
não ficará satisfeita com um valor do tipoInt
.Para adicionar adicionalmente a @VonC, uma resposta excelente e informativa.
Estou surpreso que ninguém realmente mencionou o custo de manutenção, uma vez que o projeto fica maior, os desenvolvedores antigos saem e um novo chega. Go certamente é uma língua jovem.
De modo geral, tento evitar dicas quando posso, mas elas têm seu lugar e sua beleza.
Eu uso ponteiros quando:
Por exemplo:
Razões pelas quais evito dicas:
Minha regra é escrever tantos métodos encapsulados quanto possível, como:
ATUALIZAR:
Esta pergunta me inspirou a pesquisar mais sobre o tópico e escrever uma postagem no blog sobre ele https://medium.com/gophersland/gopher-vs-object-oriented-golang-4fa62b88c701
fonte