Vetores (Vec)
Esta aula aborda o tipo Vec em Rust, desde a criação e adição de elementos até indexação segura, iteração e gerenciamento de capacidade. Aprenda a usar vetores de forma eficiente e idiomática.
Vetores (Vec<T>) são uma das estruturas de dados mais utilizadas em Rust. Eles representam uma lista dinâmica, mutável e alocada no heap, capaz de armazenar elementos de um mesmo tipo. Diferente de arrays ([T; N]), o tamanho de um vetor pode crescer ou diminuir em tempo de execução. Nesta aula, exploraremos a criação, manipulação, acesso seguro, iteração e aspectos internos de capacidade e realocação.
Entender o funcionamento de Vec é essencial para escrever código Rust idiomático e eficiente. Veremos desde operações básicas até detalhes de implementação que afetam performance.
Criação e push
Para criar um vetor, podemos usar Vec::new() ou a macro vec!. A função push adiciona um elemento ao final do vetor. Exemplo:
let mut v: Vec<i32> = Vec::new();
v.push(1);
v.push(2);
v.push(3);
println!("{:?}", v); // [1, 2, 3]Também é comum inicializar com valores usando vec!:
let v = vec![1, 2, 3];
// v é imutável, mas ainda podemos lerNote que vec! é uma macro que gera um Vec com os elementos fornecidos. O tipo é inferido automaticamente.
Indexação vs get
Podemos acessar elementos de um vetor por índice usando colchetes (v[i]) ou o método get(i). A diferença fundamental é que a indexação direta causa pânico (panic) se o índice estiver fora dos limites, enquanto get retorna Option<&T>.
let v = vec![10, 20, 30];
// Indexação direta (pode panicar)
let first = &v[0]; // ok
// let out = &v[5]; // panic!
// Usando get (seguro)
match v.get(2) {
Some(val) => println!("Valor: {}", val),
None => println!("Índice inválido"),
}Recomenda-se usar get quando não houver certeza sobre a validade do índice, especialmente em código que lida com entrada do usuário ou índices calculados.
Iteração
Iterar sobre um vetor pode ser feito de várias formas. A mais comum é usar um loop for sobre uma referência ao vetor. Exemplo:
let v = vec![1, 2, 3];
// Iteração por referência (imutável)
for elem in &v {
println!("{}", elem);
}
// Iteração mutável
let mut v = vec![1, 2, 3];
for elem in &mut v {
*elem *= 2;
}
println!("{:?}", v); // [2, 4, 6]
// Consumindo o vetor (move)
for elem in v {
println!("{}", elem);
}
// v não pode mais ser usadoTambém podemos usar métodos como iter(), iter_mut(), into_iter() e adaptadores como map, filter, etc.
Capacidade e realocação
Todo vetor possui uma capacidade (capacity) que é o número de elementos que pode armazenar sem alocar mais memória. Quando a capacidade é excedida, o vetor realoca uma nova região de memória (geralmente dobrando a capacidade) e copia os elementos. Isso pode ser custoso.
let mut v: Vec<i32> = Vec::with_capacity(2);
println!("Capacidade inicial: {}", v.capacity()); // 2
v.push(1);
v.push(2);
println!("Capacidade após 2 pushes: {}", v.capacity()); // 2
v.push(3); // realoca
println!("Capacidade após 3 pushes: {}", v.capacity()); // 4 (dobrou)Para evitar realocações frequentes, podemos usar Vec::with_capacity ou reserve para alocar espaço de antemão. A capacidade nunca é menor que o comprimento (len).
Boa prática: se você sabe o número aproximado de elementos, use with_capacity para melhorar performance.
Boas práticas e observações finais
Sempre prefira métodos seguros como get quando houver dúvida sobre índices. Use iteração por referência para evitar movimentação desnecessária. Lembre-se de que Vec é uma estrutura ownership: quando um vetor é movido, seus dados também são movidos. Para compartilhamento, use referências.
Vetores são a base para muitas outras estruturas de dados em Rust. Dominar seu uso é fundamental.
Referências
- Documentação oficial de Vec
- The Rust Programming Language - Capítulo 8.1: Vetores
- Rust by Example - Vec
- The Rustonomicon - Implementando Vec
- Vec::reserve
Exercícios
Crie um vetor vazio do tipo
Stringe adicione três strings: "Rust", "é", "legal". Imprima o vetor.✓ Resposta:let mut v: Vec<String> = Vec::new(); v.push("Rust".to_string()); v.push("é".to_string()); v.push("legal".to_string()); println!("{:?}", v);Dado o vetor
let v = vec![5, 10, 15, 20];, acesse o elemento no índice 2 usando indexação direta e usandoget. O que acontece se você tentar acessar o índice 10?✓ Resposta:let v = vec![5, 10, 15, 20]; // Indexação direta println!("{}", v[2]); // 15 // get match v.get(2) { Some(val) => println!("{}", val), None => println!("None"), } // Tentativa com índice 10: // v[10] -> panic! // v.get(10) -> retorna NoneEscreva um loop que itere sobre um vetor de números e imprima o dobro de cada um. Use iteração mutável para modificar o vetor original.
✓ Resposta:let mut v = vec![1, 2, 3, 4]; for elem in &mut v { *elem *= 2; } println!("{:?}", v); // [2, 4, 6, 8]Crie um vetor com capacidade inicial 10. Adicione 5 elementos e verifique a capacidade. Explique por que a capacidade não mudou.
✓ Resposta:let mut v: Vec<i32> = Vec::with_capacity(10); println!("Capacidade inicial: {}", v.capacity()); // 10 v.push(1); v.push(2); v.push(3); v.push(4); v.push(5); println!("Capacidade após 5 pushes: {}", v.capacity()); // 10 // A capacidade não mudou porque o número de elementos (5) é menor que a capacidade (10). // Realocação só ocorre quando len == capacity e um novo push é feito.Escreva uma função que recebe um vetor de inteiros e retorna a soma de todos os elementos. Use iteração por referência.
✓ Resposta:fn soma(v: &Vec<i32>) -> i32 { let mut total = 0; for elem in v { total += elem; } total } fn main() { let v = vec![1, 2, 3, 4, 5]; println!("{}", soma(&v)); // 15 }