O que é Array em Go?
Um array em Go é uma sequência de tamanho fixo de elementos do mesmo tipo, onde o tamanho faz parte da definição do tipo. Isso significa que [3]int e [5]int são tipos completamente diferentes — não intercambiáveis — e o tamanho é definido em tempo de compilação.
Diferente de linguagens como Python ou JavaScript, onde “array” geralmente se refere a listas dinâmicas, arrays em Go são estruturas de dados de tamanho fixo e valor. Quando você atribui um array a outra variável ou passa como argumento para uma func, todo o conteúdo é copiado — não apenas uma referência.
Na prática do dia a dia, a maioria dos programadores Go usa slices em vez de arrays. Porém, arrays são a base sobre a qual slices são construídos e têm usos específicos onde seu tamanho fixo e semântica de valor são vantajosos, como em criptografia, hashing e buffers de tamanho conhecido.
Declarando arrays
Declaração básica
// Array de 5 inteiros — inicializado com zero values
var numeros [5]int // [0, 0, 0, 0, 0]
// Array com valores iniciais
cores := [3]string{"vermelho", "verde", "azul"}
// Array parcialmente inicializado
parcial := [5]int{1, 2, 3} // [1, 2, 3, 0, 0]
Syntax […] — inferência de tamanho
Go permite que o compilador determine o tamanho do array automaticamente com [...]:
// O compilador calcula o tamanho: [4]string
linguagens := [...]string{"Go", "Rust", "Python", "Java"}
// Equivalente a:
// linguagens := [4]string{"Go", "Rust", "Python", "Java"}
Inicialização por índice
Você pode inicializar elementos específicos por índice:
// Array de 10 inteiros, com valores específicos em alguns índices
a := [10]int{1: 10, 5: 50, 9: 90}
// [0, 10, 0, 0, 0, 50, 0, 0, 0, 90]
// Útil para tabelas de lookup
codigoHTTP := [...]string{
200: "OK",
301: "Moved Permanently",
404: "Not Found",
500: "Internal Server Error",
}
Array é tipo valor — copiado na atribuição
Esta é a diferença mais fundamental entre arrays e slices em Go:
func main() {
original := [3]int{1, 2, 3}
copia := original // COPIA COMPLETA — não é referência!
copia[0] = 99
fmt.Println(original) // [1, 2, 3] — não modificado
fmt.Println(copia) // [99, 2, 3]
}
O mesmo acontece ao passar arrays para funções:
func modificar(arr [3]int) {
arr[0] = 999 // modifica apenas a cópia local
}
func main() {
dados := [3]int{1, 2, 3}
modificar(dados)
fmt.Println(dados) // [1, 2, 3] — inalterado!
}
Para evitar cópia e permitir modificação, passe um ponteiro para o array:
func modificarPorRef(arr *[3]int) {
arr[0] = 999 // modifica o array original
}
func main() {
dados := [3]int{1, 2, 3}
modificarPorRef(&dados)
fmt.Println(dados) // [999, 2, 3] — modificado!
}
Comparação de arrays
Ao contrário de slices, arrays em Go podem ser comparados diretamente com == e !=:
a := [3]int{1, 2, 3}
b := [3]int{1, 2, 3}
c := [3]int{3, 2, 1}
fmt.Println(a == b) // true — mesmos elementos na mesma ordem
fmt.Println(a == c) // false
// Isso NÃO funciona com slices:
// s1 := []int{1, 2, 3}
// s2 := []int{1, 2, 3}
// fmt.Println(s1 == s2) // ERRO de compilação!
Essa propriedade torna arrays úteis como chaves em maps:
type Coordenada [2]int
mapa := map[Coordenada]string{
{0, 0}: "origem",
{1, 5}: "destino",
}
fmt.Println(mapa[Coordenada{0, 0}]) // "origem"
Arrays multidimensionais
Go suporta arrays de múltiplas dimensões:
// Matriz 3x3
var matriz [3][3]int
// Inicialização inline
tabuleiro := [3][3]string{
{"X", "O", "X"},
{"O", "X", "O"},
{"X", "O", "X"},
}
// Acessando elementos
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
fmt.Printf("%s ", tabuleiro[i][j])
}
fmt.Println()
}
Matriz identidade
func matrizIdentidade() [4][4]float64 {
var m [4][4]float64
for i := range m {
m[i][i] = 1.0
}
return m
}
Iterando sobre arrays
Use range para iterar de forma idiomática:
cores := [...]string{"vermelho", "verde", "azul", "amarelo"}
// Índice e valor
for i, cor := range cores {
fmt.Printf("Índice %d: %s\n", i, cor)
}
// Apenas valor
for _, cor := range cores {
fmt.Println(cor)
}
// Apenas índice
for i := range cores {
fmt.Println(i)
}
Quando usar arrays em vez de slices
Embora slices sejam preferidos na maioria dos casos, arrays têm vantagens específicas:
1. Criptografia e hashing
Muitas funções da standard library usam arrays de tamanho fixo:
import "crypto/sha256"
func hashSenha(senha string) [32]byte {
return sha256.Sum256([]byte(senha))
}
func main() {
hash := hashSenha("minha-senha-secreta")
fmt.Printf("%x\n", hash)
}
2. Buffers de tamanho fixo
// Buffer fixo para leitura de rede
var buf [4096]byte
n, err := conn.Read(buf[:]) // converte para slice para a interface
if err != nil {
log.Fatal(err)
}
dados := buf[:n]
3. Chaves de map
Como arrays são comparáveis, servem como chaves:
type IP [4]byte
servidores := map[IP]string{
{192, 168, 1, 1}: "gateway",
{192, 168, 1, 100}: "servidor-web",
{10, 0, 0, 1}: "vpn",
}
4. Semântica de valor desejada
Quando a cópia automática é o comportamento desejado para garantir isolamento de dados:
type Config [3]float64 // RGB color
func processar(c Config) Config {
// Trabalha com cópia — não afeta o original
c[0] *= 1.1
return c
}
Conversão entre array e slice
A conversão é simples e frequente no código Go:
// Array para slice
arr := [5]int{1, 2, 3, 4, 5}
sl := arr[:] // slice referenciando TODO o array
sl2 := arr[1:3] // slice parcial: [2, 3]
// Slice para array (Go 1.20+)
slice := []int{10, 20, 30}
var arr2 [3]int = [3]int(slice) // copia dados do slice para o array
// Ponteiro para array a partir de slice (Go 1.17+)
ptr := (*[3]int)(slice)
Essa interoperabilidade é importante ao trabalhar com APIs que exigem arrays (como funções de criptografia) enquanto o resto do código usa slices.
Tamanho de arrays e memória
O tamanho total de um array em memória é tamanho_do_elemento × número_de_elementos:
var a [1000]int64 // 1000 × 8 bytes = 8000 bytes na stack
// Arrays grandes podem causar stack overflow
// var enorme [10000000]int64 // evite arrays muito grandes na stack
Para arrays grandes, use slices (alocados no heap) ou ponteiros para arrays para evitar cópias custosas e problemas de stack.
Boas práticas com arrays
- Prefira slices para a maioria dos casos — mais flexíveis e idiomáticos
- Use arrays para tamanhos fixos conhecidos em compilação (hashes, IPs, buffers)
- Cuidado com cópias: arrays grandes são copiados inteiramente na atribuição
- Use
[...]para contagem automática de elementos em inicializações - Use como chave de map quando precisar de chaves compostas
- Converta para slice ao passar para funções que esperam
[]T
Para mais sobre estruturas de dados em Go, explore o glossário de slices e os tutoriais de Go para iniciantes.
Perguntas frequentes (FAQ)
Qual a diferença entre array e slice em Go?
Um array tem tamanho fixo que faz parte do tipo ([5]int), é copiado inteiramente na atribuição e pode ser comparado com ==. Um slice ([]int) é dinâmico, é apenas um header (ponteiro, length, capacity) referenciando um array subjacente, e não pode ser comparado diretamente. Na prática, slices são usados em 99% dos casos.
Por que arrays em Go são tipos valor?
Go segue a filosofia de tipos simples e previsíveis. Arrays como tipos valor garantem que atribuições e passagens de parâmetros criam cópias independentes, eliminando bugs de compartilhamento acidental. Quando você quer referência (compartilhamento), usa explicitamente ponteiros ou slices. Essa clareza é uma decisão de design consciente da linguagem.
Quando usar arrays em vez de slices em Go?
Use arrays quando: (1) o tamanho é fixo e conhecido em compilação (hashes SHA-256, endereços IP); (2) precisa de comparação com == (como chaves de map); (3) quer semântica de cópia automática para isolamento; (4) trabalha com APIs que exigem tamanho fixo (crypto, encoding). Para todo o resto, prefira slices.
Como converter entre array e slice em Go?
Para converter array em slice, use a syntax arr[:] que cria um slice referenciando o array inteiro. Para converter slice em array, a partir do Go 1.20 use [N]T(slice) que copia os dados. Note que arr[:] cria referência (modificar o slice modifica o array), enquanto a conversão inversa cria cópia independente.