Vec<T>: Vetor Dinâmico em Rust

Guia completo de Vec<T> em Rust: criação, inserção, remoção, iteração, ordenação e otimização de vetores dinâmicos com exemplos práticos.

O que é Vec<T>

Vec<T>, ou simplesmente “vetor”, é a coleção dinâmica mais usada em Rust. Ele armazena elementos do tipo T de forma contígua na memória heap, podendo crescer ou encolher conforme necessário. Internamente, um Vec mantém três informações: um ponteiro para os dados no heap, o tamanho atual (len) e a capacidade alocada (capacity).

Use Vec<T> sempre que precisar de uma lista de elementos cujo tamanho não é conhecido em tempo de compilação, ou que pode mudar durante a execução. É o equivalente em Rust ao ArrayList de Java, ao list de Python ou ao std::vector de C++. Para sequências de tamanho fixo conhecidas em compilação, considere usar arrays [T; N] ou slices.


Criando Vetores

fn main() {
    // Vetor vazio (precisa de anotação de tipo ou uso posterior)
    let mut vazio: Vec<i32> = Vec::new();
    vazio.push(1);

    // Usando a macro vec!
    let numeros = vec![1, 2, 3, 4, 5];

    // Repetindo um valor
    let zeros = vec![0; 10]; // 10 zeros

    // Com capacidade pré-alocada
    let mut com_capacidade = Vec::with_capacity(100);
    com_capacidade.push(42);
    println!("Len: {}, Capacidade: {}", com_capacidade.len(), com_capacidade.capacity());
    // Len: 1, Capacidade: 100

    // A partir de um iterador
    let pares: Vec<i32> = (0..10).filter(|x| x % 2 == 0).collect();
    println!("{pares:?}"); // [0, 2, 4, 6, 8]

    // A partir de um array
    let array = [10, 20, 30];
    let de_array = array.to_vec();

    // A partir de um slice
    let slice = &numeros[1..4];
    let de_slice = slice.to_vec();

    println!("{numeros:?} {zeros:?} {de_array:?} {de_slice:?}");
}

Tabela de Métodos Principais

MétodoDescriçãoExemplo
push(valor)Adiciona ao finalv.push(10)
pop()Remove e retorna o últimov.pop()Some(10)
insert(i, valor)Insere na posição iv.insert(0, 99)
remove(i)Remove e retorna o elemento na posição iv.remove(0)
swap_remove(i)Remove trocando com o último (O(1))v.swap_remove(0)
len()Número de elementosv.len()5
is_empty()Verifica se está vaziov.is_empty()
capacity()Capacidade alocadav.capacity()
contains(&val)Verifica se contém o valorv.contains(&3)
sort()Ordena in-placev.sort()
sort_by(f)Ordena com comparador personalizadov.sort_by(|a, b| b.cmp(a))
dedup()Remove duplicatas consecutivasv.dedup()
retain(f)Mantém apenas elementos que satisfazem fv.retain(|x| *x > 0)
extend(iter)Adiciona elementos de um iteradorv.extend([4, 5, 6])
truncate(n)Mantém apenas os primeiros nv.truncate(3)
clear()Remove todos os elementosv.clear()
iter()Iterador de referênciasv.iter()
iter_mut()Iterador de referências mutáveisv.iter_mut()
windows(n)Janelas deslizantes de tamanho nv.windows(2)
chunks(n)Divide em pedaços de tamanho nv.chunks(3)
split(f)Divide onde f retorna truev.split(|x| *x == 0)
binary_search(&val)Busca binária (requer vetor ordenado)v.binary_search(&5)
as_slice()Converte para &[T]v.as_slice()

Exemplos Práticos

1. Filtrando e Transformando Dados

fn main() {
    let temperaturas = vec![22.5, 35.1, 18.3, 40.0, 28.7, 15.2, 33.8];

    // Filtrar temperaturas acima de 30°C
    let quentes: Vec<f64> = temperaturas
        .iter()
        .copied()
        .filter(|&t| t > 30.0)
        .collect();
    println!("Acima de 30°C: {quentes:?}"); // [35.1, 40.0, 33.8]

    // Converter Celsius para Fahrenheit
    let fahrenheit: Vec<f64> = temperaturas
        .iter()
        .map(|c| c * 9.0 / 5.0 + 32.0)
        .collect();
    println!("Fahrenheit: {fahrenheit:.1?}");

    // Média das temperaturas
    let soma: f64 = temperaturas.iter().sum();
    let media = soma / temperaturas.len() as f64;
    println!("Média: {media:.1}°C"); // Média: 27.7°C
}

2. Ordenação Personalizada

#[derive(Debug)]
struct Aluno {
    nome: String,
    nota: f64,
}

fn main() {
    let mut alunos = vec![
        Aluno { nome: "Ana".into(), nota: 8.5 },
        Aluno { nome: "Bruno".into(), nota: 9.2 },
        Aluno { nome: "Carlos".into(), nota: 7.8 },
        Aluno { nome: "Diana".into(), nota: 9.2 },
    ];

    // Ordenar por nota (decrescente), depois por nome (crescente)
    alunos.sort_by(|a, b| {
        b.nota.partial_cmp(&a.nota)
            .unwrap()
            .then(a.nome.cmp(&b.nome))
    });

    for aluno in &alunos {
        println!("{}: {:.1}", aluno.nome, aluno.nota);
    }
    // Bruno: 9.2
    // Diana: 9.2
    // Ana: 8.5
    // Carlos: 7.8
}

3. Removendo Duplicatas

fn main() {
    let mut numeros = vec![3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5];

    // dedup só remove duplicatas CONSECUTIVAS, então ordene primeiro
    numeros.sort();
    numeros.dedup();
    println!("Sem duplicatas: {numeros:?}"); // [1, 2, 3, 4, 5, 6, 9]

    // Alternativa: usando um HashSet para preservar a ordem original
    use std::collections::HashSet;
    let numeros2 = vec![3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5];
    let mut vistos = HashSet::new();
    let unicos: Vec<i32> = numeros2
        .into_iter()
        .filter(|x| vistos.insert(*x))
        .collect();
    println!("Únicos (ordem preservada): {unicos:?}"); // [3, 1, 4, 5, 9, 2, 6]
}

4. Usando retain para Filtrar In-Place

fn main() {
    let mut tarefas = vec![
        ("Comprar leite", true),
        ("Estudar Rust", false),
        ("Lavar louça", true),
        ("Ler livro", false),
        ("Fazer exercício", false),
    ];

    // Remover tarefas já concluídas
    tarefas.retain(|(_, concluida)| !concluida);

    println!("Tarefas pendentes:");
    for (tarefa, _) in &tarefas {
        println!("  - {tarefa}");
    }
    // - Estudar Rust
    // - Ler livro
    // - Fazer exercício
}

5. Janelas Deslizantes e Chunks

fn main() {
    let dados = vec![10, 20, 30, 40, 50, 60, 70, 80, 90, 100];

    // Média móvel com janela de 3
    let medias: Vec<f64> = dados
        .windows(3)
        .map(|janela| janela.iter().sum::<i32>() as f64 / 3.0)
        .collect();
    println!("Médias móveis: {medias:.1?}");

    // Processar em lotes de 4
    for (i, lote) in dados.chunks(4).enumerate() {
        let soma: i32 = lote.iter().sum();
        println!("Lote {i}: {lote:?} (soma: {soma})");
    }
}

Dicas de Performance e Armadilhas

  1. Use with_capacity: Se você sabe (ou consegue estimar) quantos elementos vai adicionar, use Vec::with_capacity(n) para evitar realocações. Cada realocação copia todos os dados para um bloco de memória maior.

  2. swap_remove vs remove: O remove(i) desloca todos os elementos posteriores (O(n)), enquanto swap_remove(i) troca o elemento com o último e faz pop (O(1)). Use swap_remove quando a ordem não importa.

  3. Indexação fora dos limites: Acessar v[i] onde i >= v.len() causa panic. Use v.get(i) para obter um Option<&T> seguro.

  4. dedup requer ordenação prévia: O método dedup() só remove duplicatas consecutivas. Para remover todas as duplicatas, ordene o vetor primeiro com sort().

  5. Cuidado com clone() de vetores grandes: Clonar um Vec copia todos os elementos. Prefira usar referências (&Vec<T> ou, melhor ainda, &[T]) quando possível.

  6. drain para extrair subconjuntos: Use v.drain(range) para remover e consumir uma faixa de elementos sem realocar, retornando um iterador.


Veja Também