Como Filtrar Vetor em Rust

Aprenda a filtrar vetores em Rust com iter().filter(), retain(), filter_map() e partition(). Exemplos completos com closures e programação funcional.

Filtrar coleções é uma tarefa fundamental em qualquer linguagem. Rust oferece diversos métodos para filtrar dados de forma eficiente e expressiva, desde filter() em iteradores até retain() para modificação in-place. Nesta receita, você vai aprender todas as formas de filtrar vetores em Rust.

Dependências

Todas as funções de filtragem fazem parte da biblioteca padrão:

[package]
name = "receita-filtrar"
version = "0.1.0"
edition = "2021"

Código Completo

fn main() {
    // =============================================
    // 1. iter().filter() — filtragem com iterador
    // =============================================
    let numeros = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

    // Filtrar números pares
    let pares: Vec<&i32> = numeros.iter().filter(|&&x| x % 2 == 0).collect();
    println!("Pares (referências): {:?}", pares);
    // [2, 4, 6, 8, 10]

    // Usar copied() para obter valores em vez de referências
    let pares: Vec<i32> = numeros.iter().copied().filter(|&x| x % 2 == 0).collect();
    println!("Pares (valores):     {:?}", pares);

    // Filtrar com into_iter() — consome o vetor original
    let dados = vec![10, 25, 30, 45, 50];
    let maiores_que_20: Vec<i32> = dados.into_iter().filter(|&x| x > 20).collect();
    println!("Maiores que 20:      {:?}", maiores_que_20);
    // dados não pode mais ser usado aqui

    // =============================================
    // 2. retain() — filtragem in-place (modifica o vetor)
    // =============================================
    let mut frutas = vec!["banana", "abacaxi", "maçã", "ameixa", "abacate"];

    // Manter apenas frutas que começam com 'a'
    frutas.retain(|f| f.starts_with('a'));
    println!("\nFrutas com 'a':    {:?}", frutas);
    // ["abacaxi", "ameixa", "abacate"]

    let mut valores = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    valores.retain(|&x| x % 3 == 0); // manter múltiplos de 3
    println!("Múltiplos de 3:    {:?}", valores);
    // [3, 6, 9]

    // =============================================
    // 3. filter_map() — filtrar e transformar ao mesmo tempo
    // =============================================
    let textos = vec!["42", "olá", "7", "mundo", "100", "abc"];

    // Tentar converter para i32, mantendo apenas os que convertem
    let numeros: Vec<i32> = textos
        .iter()
        .filter_map(|s| s.parse::<i32>().ok())
        .collect();
    println!("\nConvertidos: {:?}", numeros);
    // [42, 7, 100]

    // filter_map com lógica customizada
    let nomes = vec!["Ana Silva", "Bia", "Carlos Souza Santos", ""];

    let primeiros_nomes: Vec<&str> = nomes
        .iter()
        .filter_map(|nome| {
            if nome.is_empty() {
                None
            } else {
                Some(nome.split_whitespace().next().unwrap())
            }
        })
        .collect();
    println!("Primeiros nomes: {:?}", primeiros_nomes);
    // ["Ana", "Bia", "Carlos"]

    // =============================================
    // 4. partition() — dividir em dois grupos
    // =============================================
    let numeros = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

    let (pares, impares): (Vec<i32>, Vec<i32>) = numeros
        .iter()
        .partition(|&&x| x % 2 == 0);
    println!("\nPares:   {:?}", pares);
    println!("Ímpares: {:?}", impares);

    // Particionar structs
    #[derive(Debug)]
    struct Tarefa {
        titulo: String,
        concluida: bool,
    }

    let tarefas = vec![
        Tarefa { titulo: "Estudar Rust".into(), concluida: true },
        Tarefa { titulo: "Fazer deploy".into(), concluida: false },
        Tarefa { titulo: "Escrever testes".into(), concluida: true },
        Tarefa { titulo: "Revisar PR".into(), concluida: false },
    ];

    let (concluidas, pendentes): (Vec<&Tarefa>, Vec<&Tarefa>) = tarefas
        .iter()
        .partition(|t| t.concluida);

    println!("\nConcluídas:");
    for t in &concluidas {
        println!("  [x] {}", t.titulo);
    }
    println!("Pendentes:");
    for t in &pendentes {
        println!("  [ ] {}", t.titulo);
    }

    // =============================================
    // 5. Combinando filter com outros adaptadores
    // =============================================
    let palavras = vec!["Rust", "é", "rápido", "seguro", "e", "produtivo"];

    // Filtrar palavras longas, converter para maiúsculas
    let resultado: Vec<String> = palavras
        .iter()
        .filter(|p| p.len() > 3)
        .map(|p| p.to_uppercase())
        .collect();
    println!("\nPalavras longas em maiúsculas: {:?}", resultado);
    // ["RUST", "RÁPIDO", "SEGURO", "PRODUTIVO"]

    // Filtrar, enumerar e coletar com índice
    let numeros = vec![10, 5, 20, 3, 15, 8, 25];
    let grandes: Vec<(usize, &i32)> = numeros
        .iter()
        .enumerate()
        .filter(|(_, &v)| v > 10)
        .collect();
    println!("Maiores que 10 (com índice): {:?}", grandes);
    // [(2, 20), (4, 15), (6, 25)]

    // =============================================
    // 6. take_while() e skip_while() — filtragem posicional
    // =============================================
    let dados = vec![2, 4, 6, 7, 8, 10, 12];

    let enquanto_par: Vec<&i32> = dados.iter().take_while(|&&x| x % 2 == 0).collect();
    println!("\ntake_while par: {:?}", enquanto_par);
    // [2, 4, 6] — para no primeiro ímpar

    let depois_impar: Vec<&i32> = dados.iter().skip_while(|&&x| x % 2 == 0).collect();
    println!("skip_while par: {:?}", depois_impar);
    // [7, 8, 10, 12] — pula todos os pares iniciais

    // =============================================
    // 7. Contar e verificar com predicados
    // =============================================
    let notas = vec![7.5, 8.0, 5.5, 9.0, 6.0, 4.5, 8.5];

    let aprovados = notas.iter().filter(|&&n| n >= 6.0).count();
    let reprovados = notas.iter().filter(|&&n| n < 6.0).count();
    println!("\nAprovados: {} | Reprovados: {}", aprovados, reprovados);

    // any() e all() — verificar predicados
    let todos_passaram = notas.iter().all(|&n| n >= 6.0);
    let algum_dez = notas.iter().any(|&n| n == 10.0);
    println!("Todos aprovados? {} | Alguém tirou 10? {}", todos_passaram, algum_dez);
}

Saída do Programa

Pares (referências): [2, 4, 6, 8, 10]
Pares (valores):     [2, 4, 6, 8, 10]
Maiores que 20:      [25, 30, 45, 50]

Frutas com 'a':    ["abacaxi", "ameixa", "abacate"]
Múltiplos de 3:    [3, 6, 9]

Convertidos: [42, 7, 100]
Primeiros nomes: ["Ana", "Bia", "Carlos"]

Pares:   [2, 4, 6, 8, 10]
Ímpares: [1, 3, 5, 7, 9]

Concluídas:
  [x] Estudar Rust
  [x] Escrever testes
Pendentes:
  [ ] Fazer deploy
  [ ] Revisar PR

Palavras longas em maiúsculas: ["RUST", "RÁPIDO", "SEGURO", "PRODUTIVO"]
Maiores que 10 (com índice): [(2, 20), (4, 15), (6, 25)]

take_while par: [2, 4, 6]
skip_while par: [7, 8, 10, 12]

Aprovados: 5 | Reprovados: 2
Todos aprovados? false | Alguém tirou 10? false

Quando Usar Cada Método

MétodoModifica originalUso
filter()Nao (cria novo)Filtragem funcional com iteradores
retain()Sim (in-place)Remover elementos do vetor sem criar cópia
filter_map()NaoFiltrar e transformar em uma única operação
partition()NaoDividir coleção em dois grupos
take_while()NaoPegar elementos enquanto condição for verdadeira
skip_while()NaoPular elementos enquanto condição for verdadeira

Veja Também