Slice vazio em Go: inicialização correta com exemplos

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

Slice vazio em Go: como não errar desde o início

\n\n

Todo desenvolvedor Go, mais cedo ou mais tarde, se depara com a pergunta: "como inicializar corretamente um slice vazio?". Parece uma tarefa simples, mas não é tão simples assim. No FAQ oficial do Go e no Stack Overflow, centenas de comentários e milhares de votos são dedicados a este tópico. Por quê?

\n\n

Porque em Go existem duas maneiras fundamentalmente diferentes de criar um slice vazio, e a escolha entre elas afeta o desempenho, a legibilidade do código e até mesmo o comportamento durante a serialização. Se você programa em Go e quer escrever código limpo e eficiente, sem surpresas — este artigo é para você.

\n\n

Vamos analisar:

\n
    \n
  • A diferença entre var s []int e s := make([]int, 0)
  • \n
  • Quando usar nil-slice e quando usar empty slice
  • \n
  • Como isso afeta JSON, memória e desempenho
  • \n
  • O que dizem os gurus do Go e os autores da biblioteca padrão
  • \n
\n\n

Dois irmãos gêmeos: nil slice vs empty slice

\n\n

Em Go, um slice é uma estrutura de três campos: ponteiro para o array, comprimento e capacidade. Quando falamos de slice vazio, nos referimos a um slice com comprimento 0. Mas a forma como o criamos faz diferença.

\n\n

Método 1: Declaração var (nil slice)

\n\n
var s []int  // s == nil, len(s) == 0, cap(s) == 0
\n\n

Este é um nil-slice. Ele não aponta para nenhum array na memória. No entanto, você pode usar append nele sem problemas — o Go alocará memória automaticamente.

\n\n

Método 2: Inicialização com make (empty slice)

\n\n
s := make([]int, 0)  // s != nil, len(s) == 0, cap(s) == 0
\n\n

Este é um empty slice. Ele já aponta para um array de tamanho zero (um zerobase especial no runtime do Go).

\n\n

À primeira vista, a diferença é imperceptível. Mas vamos aprofundar.

\n\n

Saída no console e JSON — a primeira armadilha

\n\n

Tente executar este código:

\n\n
package main\n\nimport (\n    "encoding/json"\n    "fmt"\n)\n\nfunc main() {\n    var nilSlice []int\n    emptySlice := make([]int, 0)\n\n    fmt.Printf("nilSlice: %v, is nil: %v\\n", nilSlice, nilSlice == nil)\n    fmt.Printf("emptySlice: %v, is nil: %v\\n", emptySlice, emptySlice == nil)\n\n    // Serialização JSON\n    nilJSON, _ := json.Marshal(nilSlice)\n    emptyJSON, _ := json.Marshal(emptySlice)\n\n    fmt.Printf("nilSlice JSON: %s\\n", nilJSON)\n    fmt.Printf("emptySlice JSON: %s\\n", emptyJSON)\n}
\n\n

O resultado vai te surpreender:

\n\n
nilSlice: [], is nil: true\nemptySlice: [], is nil: false\nnilSlice JSON: null\nemptySlice JSON: []
\n\n

Aí está a grande surpresa! A serialização JSON se comporta de forma diferente. Se sua API espera [] na resposta e você passa um nil-slice — o cliente receberá null. E isso quebra os contratos.

\n\n

Muitos clientes REST (especialmente em JavaScript) verificam rigorosamente: se um array é esperado na resposta e chega null — pode ocorrer um erro ou a lógica no frontend pode ser comprometida.

\n\n

Desempenho: há diferença?

\n\n

A resposta curta: sim, mas em 99% dos casos é insignificante.

\n\n

Quando você usa make([]int, 0), o Go aloca imediatamente memória para a estrutura do slice (24 bytes em sistemas 64 bits) e a vincula ao zerobase. Ao usar var, a alocação ocorre apenas no primeiro append.

\n\n

Se você cria um slice que provavelmente permanecerá vazio, o nil-slice economiza uma alocação de memória. Se você sabe que adicionará elementos — make com especificação de capacidade oferece melhor desempenho:

\n\n
// Se você sabe o tamanho aproximado — especifique a capacidade\ns := make([]int, 0, 100)  // capacidade 100, comprimento 0\nfor i := 0; i < 100; i++ {\n    s = append(s, i)  // sem realocações!\n}
\n\n

Mas para a maioria das tarefas aplicadas, a diferença é

Blogs

Book Recommendations