Um array em Rust, escrito como [T; N], é uma coleção de tamanho fixo com N elementos do tipo T. Diferente do Vec, o tamanho do array é conhecido em tempo de compilação e faz parte do seu tipo. Arrays são alocados na stack (quando declarados como variáveis locais), o que os torna extremamente rápidos para acessar e iterar.
Quando Usar Arrays
Use arrays quando:
- O tamanho é conhecido em tempo de compilação e não muda.
- Precisa de alocação na stack (sem heap) para máxima performance.
- Trabalha com dados de tamanho fixo: coordenadas, pixels, buffers de tamanho conhecido.
- Quer garantia em tempo de compilação de que o tamanho está correto.
Se o tamanho pode variar em tempo de execução, use Vec<T>. Para referências a sequências de tamanho variável, use slices (&[T]).
Criando e Inicializando
fn main() {
// Tipo explícito: [T; N]
let numeros: [i32; 5] = [1, 2, 3, 4, 5];
// Tipo inferido pelo compilador
let nomes = ["Ana", "Bruno", "Carla"];
// Inicializar todos os elementos com o mesmo valor
let zeros = [0u8; 1024]; // 1024 bytes, todos zero
let tracos = ['-'; 40]; // 40 caracteres '-'
// Usando std::array::from_fn para inicialização computada
let quadrados: [i32; 10] = std::array::from_fn(|i| (i as i32 + 1).powi(2));
println!("Quadrados: {:?}", quadrados);
// [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
// Array de structs
#[derive(Debug)]
struct Ponto { x: f64, y: f64 }
let pontos: [Ponto; 3] = [
Ponto { x: 0.0, y: 0.0 },
Ponto { x: 1.0, y: 2.0 },
Ponto { x: 3.0, y: 4.0 },
];
println!("{:?}", pontos);
println!("Tamanho de numeros: {}", numeros.len());
println!("Traços: {}", tracos.iter().collect::<String>());
}
Tabela de Métodos e Operações
| Operação | Descrição | Complexidade |
|---|---|---|
arr[i] | Acesso por índice (com verificação de limites) | O(1) |
arr.len() | Número de elementos | O(1) |
arr.is_empty() | Verifica se o array tem tamanho 0 | O(1) |
arr.iter() | Iterador sobre referências &T | O(n) |
arr.iter_mut() | Iterador sobre referências mutáveis &mut T | O(n) |
arr.into_iter() | Iterador que consome o array (move os elementos) | O(n) |
arr.map(f) | Aplica função a cada elemento, retorna novo array | O(n) |
arr.each_ref() | Converte &[T; N] em [&T; N] | O(n) |
arr.each_mut() | Converte &mut [T; N] em [&mut T; N] | O(n) |
arr.contains(&v) | Verifica se contém o valor (via slice) | O(n) |
arr.sort() | Ordena in-place (via slice) | O(n log n) |
arr.reverse() | Inverte a ordem (via slice) | O(n) |
arr.windows(n) | Janelas deslizantes (via slice) | O(n) |
arr.chunks(n) | Divide em pedaços (via slice) | O(n) |
std::array::from_fn(f) | Cria array com função de índice | O(n) |
Exemplos Práticos
1. Indexação e Iteração
fn main() {
let meses = [
"Janeiro", "Fevereiro", "Março", "Abril",
"Maio", "Junho", "Julho", "Agosto",
"Setembro", "Outubro", "Novembro", "Dezembro",
];
// Acesso por índice
println!("Primeiro mês: {}", meses[0]);
println!("Último mês: {}", meses[11]);
// Acesso seguro com get() (retorna Option)
match meses.get(5) {
Some(mes) => println!("Mês 6: {}", mes),
None => println!("Índice inválido"),
}
// Iteração com enumerate
println!("\nCalendário:");
for (i, mes) in meses.iter().enumerate() {
println!(" {:2}. {}", i + 1, mes);
}
// into_iter consome o array (move os elementos)
let cores = ["vermelho", "verde", "azul"];
for cor in cores {
// Em Rust 2021+, `for x in array` usa IntoIterator
println!("Cor: {}", cor);
}
}
2. map e Transformações
fn main() {
let temperaturas_c = [0.0_f64, 20.0, 37.0, 100.0];
// map: transforma cada elemento, retornando novo array do mesmo tamanho
let temperaturas_f = temperaturas_c.map(|c| c * 9.0 / 5.0 + 32.0);
println!("Celsius: {:?}", temperaturas_c);
println!("Fahrenheit: {:?}", temperaturas_f);
// Converter array de strings para array de tamanhos
let palavras = ["Rust", "é", "incrível"];
let tamanhos = palavras.map(|p| p.len());
println!("Tamanhos: {:?}", tamanhos); // [4, 2, 9]
// from_fn: criar array com lógica complexa
let fibonacci: [u64; 10] = std::array::from_fn(|i| {
if i < 2 {
return i as u64;
}
let mut a = 0u64;
let mut b = 1u64;
for _ in 2..=i {
let temp = a + b;
a = b;
b = temp;
}
b
});
println!("Fibonacci: {:?}", fibonacci);
// [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
}
3. Arrays como Slices e Métodos de Slice
fn main() {
let mut numeros = [5, 3, 8, 1, 9, 2, 7, 4, 6];
// Arrays implementam Deref<Target=[T]>, então métodos de slice funcionam
println!("Contém 7? {}", numeros.contains(&7)); // true
println!("Começa com [5, 3]? {}", numeros.starts_with(&[5, 3])); // true
// Ordenar (modifica in-place via slice)
numeros.sort();
println!("Ordenado: {:?}", numeros); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
// Reverter
numeros.reverse();
println!("Reverso: {:?}", numeros); // [9, 8, 7, 6, 5, 4, 3, 2, 1]
// Janelas deslizantes
let dados = [10, 20, 30, 40, 50];
println!("\nJanelas de tamanho 3:");
for janela in dados.windows(3) {
let media: f64 = janela.iter().sum::<i32>() as f64 / 3.0;
println!(" {:?} -> média = {:.1}", janela, media);
}
// Fatias (slices) de arrays
let primeiros_tres = &dados[..3];
let ultimos_dois = &dados[3..];
println!("\nPrimeiros 3: {:?}", primeiros_tres);
println!("Últimos 2: {:?}", ultimos_dois);
}
4. Const Generics — Funções Genéricas sobre Tamanho
// Função genérica que aceita arrays de qualquer tamanho
fn soma<const N: usize>(arr: &[f64; N]) -> f64 {
arr.iter().sum()
}
fn media<const N: usize>(arr: &[f64; N]) -> f64 {
soma(arr) / N as f64
}
fn imprimir_array<T: std::fmt::Debug, const N: usize>(arr: &[T; N]) {
println!("[{}] {:?}", N, arr);
}
fn main() {
let notas_3 = [8.5, 9.0, 7.5];
let notas_5 = [8.0, 9.5, 7.0, 8.5, 6.5];
// A mesma função funciona para arrays de tamanhos diferentes
println!("Soma (3 notas): {:.1}", soma(¬as_3));
println!("Média (3 notas): {:.2}", media(¬as_3));
println!("Soma (5 notas): {:.1}", soma(¬as_5));
println!("Média (5 notas): {:.2}", media(¬as_5));
imprimir_array(&[1, 2, 3]);
imprimir_array(&["a", "b", "c", "d"]);
}
5. Padrões Comuns com Arrays
fn main() {
// Desestruturação
let rgb = [255u8, 128, 0];
let [r, g, b] = rgb;
println!("R={}, G={}, B={}", r, g, b);
// Comparação (arrays implementam PartialEq)
let a = [1, 2, 3];
let b = [1, 2, 3];
let c = [1, 2, 4];
println!("a == b? {}", a == b); // true
println!("a == c? {}", a == c); // false
// Array bidimensional (matriz)
let matriz: [[f64; 3]; 3] = [
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 1.0],
];
println!("\nMatriz identidade 3x3:");
for linha in &matriz {
println!(" {:?}", linha);
}
// Converter array em Vec
let arr = [10, 20, 30];
let vec: Vec<i32> = arr.into(); // ou arr.to_vec()
println!("\nVec: {:?}", vec);
// Tentar converter slice em array
let slice: &[i32] = &[1, 2, 3];
let arr: [i32; 3] = slice.try_into().expect("slice com tamanho errado");
println!("Array de slice: {:?}", arr);
// Usar array como buffer fixo
let mut buffer = [0u8; 256];
let mensagem = b"Ola, Rust!";
buffer[..mensagem.len()].copy_from_slice(mensagem);
println!(
"Buffer: {}",
std::str::from_utf8(&buffer[..mensagem.len()]).unwrap()
);
}
Características de Desempenho
| Operação | Array [T; N] | Vec<T> | Slice &[T] |
|---|---|---|---|
| Alocação | Stack | Heap | Referência |
| Tamanho em runtime | Fixo (compilação) | Dinâmico | Dinâmico |
| Acesso por índice | O(1) | O(1) | O(1) |
| Overhead de memória | 0 bytes | 24 bytes (ptr+len+cap) | 16 bytes (ptr+len) |
| Cache-friendly | Sim | Sim | Sim |
| Crescimento | Impossível | push() O(1) amort. | Impossível |
Memória: Arrays são alocados diretamente na stack frame da função (quando são variáveis locais). Isso significa zero overhead de alocação, mas cuidado: arrays muito grandes na stack podem causar stack overflow. Para arrays grandes (dezenas de KB ou mais), considere usar Vec ou Box<[T; N]>.
Tamanho do tipo: O tamanho de [T; N] é exatamente N * size_of::<T>() bytes. Por exemplo, [i32; 100] ocupa 400 bytes na stack.
Arrays vs Vec vs Slices
| Critério | [T; N] | Vec<T> | &[T] |
|---|---|---|---|
| Tamanho | Fixo (compilação) | Dinâmico | Dinâmico |
| Alocação | Stack | Heap | Referência |
| Pode crescer | Nao | Sim | Nao |
| Tipo de ownership | Owned | Owned | Borrowed |
| Uso típico | Buffers fixos, coords | Listas dinâmicas | Parâmetros de função |
| Coerção para slice | &arr -> &[T] | &vec -> &[T] | Já é slice |
Regra prática: Use arrays quando o tamanho é fixo e conhecido. Use Vec quando precisa de tamanho dinâmico. Use &[T] como parâmetro de função para aceitar tanto arrays quanto vetores.
// Função que aceita qualquer sequência (array, Vec, slice)
fn processar(dados: &[i32]) {
println!("Processando {} elementos", dados.len());
}
fn main() {
let array = [1, 2, 3];
let vetor = vec![4, 5, 6];
processar(&array); // array -> &[i32]
processar(&vetor); // Vec -> &[i32]
processar(&[7, 8]); // slice literal
}
Veja Também
- Slices em Rust — fatias de tamanho dinâmico
- Vec em Rust — vetor dinâmico alocado no heap
- Tuplas em Rust — outro tipo de tamanho fixo com tipos heterogêneos
- Ranges em Rust — faixas para indexação e iteração
- Variáveis, Tipos e Funções — fundamentos dos tipos em Rust
- Filtrar Vetor em Rust — técnicas de filtragem aplicáveis a slices