Recentemente, tive a incrível oportunidade de participar de um dos maiores eventos de Golang da América Latina, a Gophercon BR. Gostaria de expressar minha gratidão aos organizadores e a todos que estiveram presentes no evento, pois proporcionaram um ambiente incrível para networking e aprendizado.
Durante o evento, tive um encontro interessante com um rapaz chamado João Salvador. Ele apresentou um desafio intrigante, exibindo um código que imediatamente despertou minha curiosidade e fez meus neurônios entrarem em ação para decifrá-lo. A experiência de resolver esse desafio foi estimulante e educativa, e, após concluí-lo, senti a necessidade de compartilhar esse conhecimento com a comunidade.
Neste post, estou entusiasmado em apresentar o desafio proposto por João Salvador e explicar os detalhes por trás dele. Acredito que a compreensão deste desafio trará uma nova perspectiva sobre o uso de receivers com ou sem ponteiro em Go, proporcionando insights valiosos que podem expandir nossos horizontes e ajudar muitas pessoas a aprofundar seu entendimento da linguagem.
Desafio que me foi Apresentado no Evento
O que você espera que o console imprima? Pense bem antes de continuar lendo, isso vai enriquecer ainda mais sua compreensão no tópico em questão
Comente a sua resposta!
Agora iremos juntos entender e desvendar a solução
Vamos começar nossa jornada entendendo que temos uma estrutura chamada "Person". Cada instância dessa estrutura possui um atributo chamado "Name", que armazena o nome de uma pessoa. Além disso, esta estrutura é enriquecida com um método chamado "SayHi", que é acionado por meio de um conceito conhecido como "receiver". A função principal deste método é simples, ele imprime na tela uma saudação, "Hi", seguida pelo nome da pessoa.
// Declarada a estrutura Person
type Person struct {
Name string
}
// SayHi vai printar uma saudação Hi seguida com o nome da pessoa
func (e Person) SayHi() {
fmt.Printf("Hi %s!\n", e.Name)
}
Tá! Mas o que é receiver?
É um parâmetro especial em um método de um tipo definido pelo usuário (struct) que permite que esse método seja chamado em instâncias desse tipo. O receiver é semelhante ao conceito de um objeto em orientação a objetos, mas é específico para métodos em Go.
Observamos que o método SayHi
utiliza um receiver de valor, o que significa que, nesse contexto, o método atua em uma cópia do valor original do tipo. Isso implica que qualquer alteração efetuada dentro do método não terá efeito sobre a instância original desse tipo.
Agora que entendemos isso vamos ver como funciona um receiver
de ponteiro. Ou seja, o método opera diretamente na instância original do tipo, pois recebe um ponteiro para ela. Isso permite que o método modifique o valor da instância original.
func (p *Person) UpdateName(newName string) {
p.Name = newName
}
Agora que entendemos ambos os casos comentado a cima, vamos criar nossa func main()
para visualizar e entender os outputs gerados.
func main() {
var p = Person{Name: "Renan"}
defer p.SayHi() // Output: Hi Renan!
defer func() { p.SayHi() }() // Output: Hi Renan Bastos!
p.UpdateName("Renan Bastos")
}
Parece bruxaria mas não é, os dois defer
imprimiram valores diferentes porque o defer empilha uma cópia da estrutura no primeiro e uma função anônima no segundo. Portanto, quando o defer é executado, ele já capturou a alteração no campo Name. O defer funciona como uma pilha, executando o último defer chamado primeiro e desempilhando conforme cada um é executado.
// Output final
// Output: Hi Renan Bastos!
// Output: Hi Renan!
Conclusão
Neste artigo, exploramos o conceito de receiver em Go e a utilidade do defer. Uma questão frequente é quando devemos optar por usar um receiver de ponteiro. A escolha depende das necessidades específicas de seu programa e do comportamento desejado, e não existe uma resposta única. Portanto, a melhor abordagem é avaliar cuidadosamente o contexto de seu código e escolher entre receiver de valor ou de ponteiro com base nas exigências do projeto.
Top comments (0)