make() vs new() em Go: o que escolher em 2025? Guia claro

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Make ou New? A eterna questão do desenvolvedor Go



Você programa em Go há uma semana ou um ano, mas ainda às vezes hesita por um segundo antes de escolher: make() ou new()? Isso é normal. A pergunta "Why would I make() or new()?" no Stack Overflow acumulou dezenas de milhares de visualizações e continua relevante mesmo em 2025. Por quê? Porque por trás da aparente simplicidade, existe uma diferença fundamental na filosofia de gerenciamento de memória em Go.



Vamos entender de uma vez por todas o que é o quê, para que você nunca mais tenha dúvidas. Iremos do básico aos casos reais, destacaremos armadilhas e daremos uma lista de verificação para o trabalho diário. Vamos lá!



Resposta rápida (para quem está com pressa)



Se você precisa alocar memória para um slice, map ou channel — use make(). Em todos os outros casos — use new() ou o literal &Type{}. Mas o diabo, como sempre, está nos detalhes.



O que é new() e como funciona



A função new(T) é uma função embutida do Go que aloca memória para o tipo T, zera (valor zero) e retorna um ponteiro para essa memória: *T.



Simplificando: new() fornece um objeto vazio, mas válido. Você obtém um ponteiro, mas o objeto em si ainda não está pronto para uso completo se precisar de inicialização de estruturas internas.



package main

import "fmt"

type Person struct { Name string Age int}

func main() { p := new(Person) fmt.Println(p) // Saída: &{ 0} fmt.Println(p.Name == "") // true}


Aqui, new(Person) alocou memória para a struct, zerou os campos (string vazia e 0) e retornou um ponteiro. Tudo certo.



O que é make() e seu superpoder



make() também é uma função embutida, mas funciona apenas com três tipos: slices, maps e channels. Por que apenas eles? Porque esses tipos têm uma estrutura interna (cabeçalho) que precisa ser inicializada, não apenas zerada.



make() não retorna um ponteiro. Ele retorna um valor inicializado do tipo desejado (não um ponteiro!).



// Slice: comprimento 5, capacidade 10s := make([]int, 5, 10)fmt.Println(len(s), cap(s)) // 5 10

// Map: pronto para inserir elementosm := make(map[string]int)m["key"] = 42fmt.Println(m) // map[key:42]

// Channel: bufferizado para 3 mensagensch := make(chan int, 3)ch <- 1fmt.Println(<-ch) // 1


Observe: se você tentar escrever m := new(map[string]int), obterá um ponteiro para um map nulo. Inserir um elemento nesse map causará pânico (panic).



Por que new() não funciona para slices, maps e channels?



Vamos ver no exemplo de um slice. Um slice em Go é uma struct de três campos: ponteiro para o array, comprimento e capacidade. new([]int) alocará memória para essa struct e a zerará. O ponteiro será nil, comprimento e capacidade serão 0. Você obterá &[] — um ponteiro para um slice nulo. Ele não quebrará ao atribuir por índice? Não, vai falhar.



make([]int, 3) faz três coisas:

  • Aloca internamente um array real de 3 elementos (ou mais, se a capacidade for especificada).
  • Inicializa o cabeçalho do slice (pointer, length, capacity).
  • Retorna um slice pronto para uso.


O mesmo acontece com map: make(map[string]int) cria uma tabela hash e aloca memória para os buckets. new(map[string]int) fornece apenas um map nulo.



Quando usar new() em 2025?



A prática moderna na comunidade Go é: new() é usado raramente. Na maioria das vezes, os desenvolvedores preferem literais com obtenção de endereço: &Type{}. Isso é mais legível e permite definir campos imediatamente.

Blogs

Book Recommendations