
Se você está aprendendo Go, entender slices é essencial, porque eles aparecem em praticamente todo código real na linguagem.
O que é um slice
Um slice é uma visão dinâmica sobre um array. Ele não armazena os dados diretamente, mas sim uma referência a uma região contígua de memória, junto com informações sobre tamanho e capacidade.
A sintaxe básica é:
var s []int
Isso declara um slice de inteiros. Diferente de um array, você não precisa especificar o tamanho no momento da declaração.
Criando slices
Existem várias formas de criar slices em Go:
// Slice vazio
s1 := []int{} // Slice com valores iniciais
s2 := []int{1, 2, 3, 4, 5} // Usando make (tamanho e capacidade)
s3 := make([]int, 5) // tamanho 5, capacidade 5
s4 := make([]int, 3, 10) // tamanho 3, capacidade 10
arr := [5]int{1, 2, 3, 4, 5} // A partir de um array
s5 := arr[1:4] // slice do índice 1 ao 3
A função make é muito usada quando você sabe o tamanho inicial, mas quer que o slice possa crescer depois.
Tamanho e capacidade
Todo slice tem duas propriedades importantes:
- Len (tamanho): número de elementos no slice.
- Cap (capacidade): número total de elementos que o slice pode conter sem precisar realocar memória.
s := make([]int, 3, 10)
fmt.Println(len(s)) // 3
fmt.Println(cap(s)) // 10
Entender a diferença entre tamanho e capacidade é importante para escrever código eficiente, especialmente quando o slice vai crescer muito.
Adicionando elementos
Para adicionar elementos a um slice, usamos a função append:
s := []int{1, 2, 3}
s = append(s, 4)
s = append(s, 5, 6, 7)
fmt.Println(s)
O append pode adicionar um ou vários elementos de uma vez. Se a capacidade for insuficiente, o Go automaticamente aloca um novo array maior e copia os elementos.
Fatiamendo (slicing)
Uma das operações mais comuns com slices é criar novos slices a partir de um existente, usando a sintaxe de fatiamento:
s := []int{0, 1, 2, 3, 4, 5}
s1 := s[1:4] // [1 2 3]
s2 := s[:3] // [0 1 2]
s3 := s[3:] // [3 4 5]
s4 := s[:] // cópia superficial de todo o slice
A sintaxe é [inicio:fim], onde inicio é inclusivo e fim é exclusivo.
Iterando sobre slices
Para percorrer um slice, usamos o for com range:
s := []string{"Go", "Rust", "Python"}
for i, v := range s {
fmt.Printf("Índice %d: %s\n", i, v)
}
Se você só precisa do valor, pode ignorar o índice:
for _, v := range s {
fmt.Println(v)
}
Slices são referências
Um ponto importante é que slices são referências para o array subjacente. Isso significa que modificar um slice pode afetar outro slice que compartilhe o mesmo array:
s1 := []int{1, 2, 3, 4, 5}
s2 := s1[1:4] // [2 3 4]
s2[0] = 99
fmt.Println(s1) // [1 99 3 4 5]
Isso é eficiente, mas exige cuidado para não causar efeitos colaterais inesperados.
Copiando slices
Se você precisa de uma cópia independente, use a função copy:
s1 := []int{1, 2, 3, 4, 5}
s2 := make([]int, len(s1))
copy(s2, s1)
s2[0] = 99
fmt.Println(s1) // [1 2 3 4 5]
fmt.Println(s2) // [99 2 3 4 5]
A função copy retorna o número de elementos copiados.
Slices de slices (matrizes dinâmicas)
Você pode criar slices de slices para representar matrizes ou tabelas:
matriz := [][]int{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
}
fmt.Println(matriz[1][2]) // 6
Isso é muito usado para representar dados bidimensionais de forma flexível.
Boas práticas
Algumas dicas para trabalhar bem com slices:
- Use
makequando souber o tamanho: isso evita realocações desnecessárias. - Cuidado ao fatiar: slices compartilham o array subjacente.
- Use
copypara cópias reais: não atribua slices se precisar de independência. - Prefira
append: é seguro e lida com realocação automaticamente. - Monitore a capacidade: em código crítico, entender
lenecapajuda a otimizar.
Exemplo de pré-alocação eficiente:
// Ruim: muitas realocações
var s []int
for i := 0; i < 1000; i++ {
s = append(s, i)
}
// Bom: aloca uma vez
s := make([]int, 0, 1000)
for i := 0; i < 1000; i++ {
s = append(s, i)
}
Conclusão
Slices são uma das estruturas mais importantes em Go. Eles combinam flexibilidade, eficiência e simplicidade, mas exigem entendimento sobre como funcionam por baixo dos panos, especialmente em relação a capacidade, realocação e compartilhamento de memória.
Dominar slices é fundamental para escrever código Go eficiente e evitar bugs sutis relacionados a referências e modificações inesperadas.


