Quando escrevemos métodos em Go, uma das decisões importantes é se devemos passar a struct por valor ou por ponteiro. A escolha pode impactar a performance, o comportamento do nosso código e a alocação de memória. Neste post, vamos explorar essa diferença com um exemplo prático e entender em quais situações cada abordagem é mais adequada.
Vamos começar com uma pequena struct e dois métodos: um onde a struct é passada por valor e outro por ponteiro.
package main
import (
"fmt"
)
type Person struct {
Name string
Age int
}
// Método com struct passada por valor
func (p Person) CelebrateBirthdayValue() {
p.Age++
}
// Método com struct passada por ponteiro
func (p *Person) CelebrateBirthdayPointer() {
p.Age++
}
func main() {
person := Person{Name: "Alice", Age: 30}
// Passando por valor
person.CelebrateBirthdayValue()
fmt.Println("Após CelebrateBirthdayValue:", person.Age) // Saída: 30
// Passando por ponteiro
person.CelebrateBirthdayPointer()
fmt.Println("Após CelebrateBirthdayPointer:", person.Age) // Saída: 31
}
Diferença entre valor e ponteiro
Quando passamos uma struct por valor para um método, o Go cria uma cópia da struct. Qualquer alteração feita na struct dentro do método não afetará a struct original, pois estamos manipulando uma cópia independente.
Por outro lado, quando passamos uma struct por ponteiro, estamos passando o endereço de memória da struct original. Isso significa que qualquer alteração feita na struct dentro do método afetará diretamente a struct original, pois estamos manipulando a mesma instância.
Em resumo:
- Por Valor: O método recebe uma cópia da struct, criando um novo espaço de memória.
- Por Ponteiro: O método recebe o endereço de memória da struct original, apontando para o mesmo espaço de memória.
Heap
Quando uma struct é passada por valor, a cópia criada é alocada na stack, o que geralmente é rápido e eficiente. No entanto, se a struct for grande, essa cópia pode consumir muita memória da stack.
Quando uma struct é passada por ponteiro, o ponteiro em si é alocado na stack, mas a struct original pode estar alocada na heap, especialmente se foi criada usando new ou make, ou se foi capturada por uma função anônima. A utilização da heap é mais custosa em termos de tempo de alocação e coleta de lixo, mas permite a manipulação eficiente de grandes quantidades de dados sem copiar toda a struct.
Quando utilizar cada abordagem
Por valor
Utilizar structs por valor é útil quando:
- Queremos garantir que a struct original não seja modificada.
- A struct é pequena e a cópia não afeta significativamente a performance.
- O método é uma simples leitura de dados sem a necessidade de alterar o estado interno.
Exemplo:
func (p Person) GetName() string {
return p.Name
}
Neste caso, GetName apenas lê o campo Name e retorna uma string, sem modificar o estado da struct.
Por ponteiro
Utilizar structs por ponteiro é benéfico quando:
- Precisamos modificar a struct original.
- A struct é grande e copiar seus dados seria custoso em termos de memória e performance.
- Queremos evitar cópias desnecessárias para melhorar a eficiência do código.
Exemplo:
func (p *Person) UpdateName(newName string) {
p.Name = newName
}
Aqui, UpdateName altera diretamente o campo Name da struct original, o que é mais eficiente do que criar uma cópia.
Conclusão
Decidir entre passar uma struct por valor ou por ponteiro ao escrever métodos em Go é uma escolha importante que pode impactar a performance, o comportamento do seu código e a alocação de memória.
Passar por valor é útil para garantir a imutabilidade da struct dentro do método, enquanto passar por ponteiro é essencial para modificar a struct original e otimizar a performance ao lidar com structs maiores.
Faça parte da comunidade!
Receba os melhores conteúdos sobre Go, Kubernetes, arquitetura de software, Cloud e esteja sempre atualizado com as tendências e práticas do mercado.
Livros Recomendados
Abaixo listei alguns dos melhores livros que já li sobre GO.

