Vec: Entendendo o Array Dinâmico Fundamental
Vec[T; n], que têm tamanho fixo e conhecido em tempo de compilação, Vec permite que você trabalhe com coleções cujo tamanho é desconhecido ou muda durante a execução.
Internamente, Vec mantém três informações críticas: um ponteiro para os dados na heap, a capacidade (quantos elementos cabem sem realocação) e o comprimento (quantos elementos estão realmente armazenados). Essa distinção entre capacidade e comprimento é essencial para entender a performance e o comportamento de Vec.
fn main() {
// Criando um Vec vazio
let mut numeros: Vec<i32> = Vec::new();
// Adicionando elementos
numeros.push(10);
numeros.push(20);
numeros.push(30);
println!("Vec: {:?}", numeros);
println!("Comprimento: {}", numeros.len());
println!("Capacidade: {}", numeros.capacity());
}
Criação, Inicialização e Operações Básicas
Existem várias formas ergonômicas de criar um Vec em Rust. A macro vec! é a mais prática para inicializar com valores conhecidos, enquanto Vec::new() cria um vetor vazio. O método with_capacity() é crucial quando você sabe antecipadamente quantos elementos será necessário armazenar, evitando realocações desnecessárias.
fn main() {
// Usando macro vec!
let mut cores = vec!["vermelho", "azul", "verde"];
// Pré-alocando capacidade
let mut dados: Vec<String> = Vec::with_capacity(100);
// Acessando elementos
println!("Primeira cor: {}", cores[0]);
// Iterando
for cor in &cores {
println!("{}", cor);
}
// Modificando
cores.push("amarelo");
cores[0] = "laranja";
// Removendo
let removido = cores.pop(); // Remove o último
println!("Removido: {:?}", removido);
}
As operações de indexação usam [], mas falham em runtime se o índice estiver fora dos limites. Para segurança, use get(), que retorna Option<&T>. O método pop() remove e retorna o último elemento, enquanto insert() e remove() trabalham com índices específicos — porém são custosos para vetores grandes porque requerem realocação de memória.
Propriedade, Empréstimo e Iteração Eficiente
A relação entre Vec e o sistema de propriedade (ownership) de Rust é onde muitos iniciantes enfrentam dificuldades. Quando você passa um Vec para uma função sem usar referência, a propriedade é transferida e a variável original fica inacessível. Use &vec para empréstimos imutáveis e &mut vec para mutáveis.
fn processar_vetor(vec: &[i32]) -> i32 {
vec.iter().sum()
}
fn duplicar_elementos(vec: &mut Vec<i32>) {
for elemento in vec.iter_mut() {
*elemento *= 2;
}
}
fn main() {
let mut numeros = vec![1, 2, 3, 4, 5];
let soma = processar_vetor(&numeros);
println!("Soma: {}", soma);
duplicar_elementos(&mut numeros);
println!("Duplicados: {:?}", numeros);
// Iteração eficiente com into_iter (consome o vetor)
let palavras = vec!["hello", "world"];
for palavra in palavras {
println!("{}", palavra);
}
// 'palavras' não está mais acessível aqui
}
A escolha entre iter(), iter_mut() e into_iter() é fundamental. Use iter() para não modificar e manter a propriedade; iter_mut() para modificar in-place; e into_iter() quando quiser consumir o vetor e obter a propriedade dos elementos. Operações que consomem o vetor como into_iter() são geralmente mais eficientes porque não precisam gerenciar referências.
Performance, Alocação de Memória e Boas Práticas
Vec crescer dinamicamente tem um custo: quando a capacidade é excedida, ele realoca a memória — copiando todos os elementos para um novo bloco maior na heap. Essa é uma operação O(n) que deve ser evitada ao máximo. Se você sabe que precisará armazenar 10.000 elementos, aloque essa capacidade desde o início com Vec::with_capacity(10_000).
fn main() {
// Ineficiente: múltiplas realocações
let mut lento = Vec::new();
for i in 0..100_000 {
lento.push(i);
}
// Eficiente: uma alocação única
let mut rapido = Vec::with_capacity(100_000);
for i in 0..100_000 {
rapido.push(i);
}
// Reserve espaço adicional se necessário
let mut reservado = vec![1, 2, 3];
reservado.reserve(1000);
// Métodos úteis
println!("Está vazio? {}", reservado.is_empty());
reservado.clear(); // Remove todos sem deallocar
println!("Após clear: len={}, capacity={}",
reservado.len(),
reservado.capacity());
}
Métodos como reserve() pré-alocam espaço sem adicionar elementos; clear() remove todos os itens mas mantém a capacidade; e shrink_to_fit() reduce a capacidade ao comprimento atual, útil quando você sabe que não adicionará mais elementos. Use slices (&[T]) em assinaturas de funções em vez de &Vec<T> — slices são mais flexíveis e aceitam tanto referências para vetores quanto para arrays primitivos.
Conclusão
Vec