Antes de criarmos as rotas para salvar e alterar dados na nossa API, precisamos dominar um conceito muito importante da linguagem Go que costuma assustar quem está começando, mas que é extremamente simples quando olhamos na prática: os Ponteiros.
Por padrão, quando passamos uma variável ou uma Struct para dentro de uma função, o Go faz uma cópia daquele objeto na memória. Isso significa que qualquer alteração feita lá dentro da função acontece apenas na cópia e morre quando a função termina, sem afetar o dado original.
Um Ponteiro nada mais é do que uma variável que armazena o endereço de memória de outra variável. Quando usamos ponteiros, nós não enviamos uma cópia do dado, mas sim o endereço direto de onde o dado original está guardado. Assim, qualquer alteração feita dentro da função altera o dado original diretamente na memória.
Para trabalhar com ponteiros no Go, usamos dois símbolos fundamentais:
*): Colocado antes de um tipo (ex: *Pessoa), ele indica que a função espera receber um ponteiro (um endereço de memória) daquela estrutura.&): Colocado antes de uma variável (ex: &pessoa), ele extrai e envia o endereço de memória exato daquela variável.Para entender a diferença de uma vez por todas, vamos criar um arquivo de teste separado ou substituir temporariamente o código do nosso projeto por este exemplo didático. Ele compara o comportamento de uma função comum com uma função que utiliza ponteiros:
// Pacote principal para execução de testes
package main
// Importação do pacote de formatação de texto
import "fmt"
// Modelo simples para o nosso teste
type Pessoa struct {
Nome string
}
// 1. Função SEM ponteiro: Recebe uma CÓPIA do objeto (Passagem por Valor)
func alterarNome1(pessoa Pessoa) {
pessoa.Nome = "Ralf (Tentativa)"
// Esta alteração acontece apenas na cópia temporária de memória!
}
// 2. Função COM ponteiro (*): Recebe o ENDEREÇO do objeto original (Passagem por Referência)
func alterarNome2(pessoa *Pessoa) {
pessoa.Nome = "Ralf (Sucesso)"
// Esta alteração modifica diretamente o objeto original na memória!
}
// Função principal
func main() {
// Criamos uma instância de Pessoa contendo o nome Henrique
pessoa := Pessoa{Nome: "Henrique"}
fmt.Println("Nome inicial:", pessoa.Nome) // Resultado: Henrique
// Executando a função SEM ponteiro
alterarNome1(pessoa)
fmt.Println("Após alterarNome1:", pessoa.Nome) // Continua: Henrique
// Executando a função COM ponteiro (passamos o endereço usando &)
alterarNome2(&pessoa)
fmt.Println("Após alterarNome2:", pessoa.Nome) // Agora é: Ralf (Sucesso)
}
Ao rodar esse programa com o comando go run main.go, o resultado exibido será:
Nome inicial: Henrique
Após alterarNome1: Henrique
Após alterarNome2: Ralf (Sucesso)
Por que isso importa para a nossa API de alunos? Nas próximas aulas, quando formos criar funções para cadastrar ou atualizar os estudantes dentro da nossa lista global (o nosso slice), precisaremos passar referências por ponteiro para garantir que o Go manipule e modifique os dados reais do sistema, e não apenas cópias perdidas na memória!