Iterator::filter_map în Rust

Transformă și filtrează într-un singur pas: păstrează valorile din Some(..) returnate de funcție și sare peste None.

Sintaxă
fn filter_map<B, F>(self, f: F) -> FilterMap<Self, F>
where F: FnMut(Self::Item) -> Option<B>

Ce face Iterator::filter_map

filter_map(|x| ...) apelează closure-ul pe fiecare element. Dacă rezultatul este Some(y), produce y; dacă este None, sare peste element. Este echivalentul lui map urmat de filter(|o| o.is_some()) și map(|o| o.unwrap()), dar mai clar și fără unwrap.

Cazul clasic: transformarea textelor în numere cu s.parse().ok(), păstrând doar ce s-a putut converti. Ca toate adaptoarele, este leneș: nu face nimic până când iteratorul nu este consumat (de for, collect, sum, count etc.).

Parametri

Parametru Descriere
f Closure-ul care returnează Some(valoare) pentru elementele păstrate și None pentru cele sărite.

Ce returnează

Un iterator FilterMap cu valorile din Some.

Exemple

Exemplul 1: Doar textele care sunt numere

parse dă un Result; .ok() îl transformă în Option, așa că valorile greșite dispar.

fn main() {
    let intrare = ["12", "abc", "7", "", "30", "4.5"];
    let numere: Vec<u32> = intrare.iter().filter_map(|s| s.parse().ok()).collect();
    println!("{:?}", numere);
    println!("suma: {}", numere.iter().sum::<u32>());
}

Rezultat

[12, 7, 30]
suma: 49

Exemplul 2: Telefoanele celor care au completat câmpul

struct Contact {
    nume: &'static str,
    telefon: Option<&'static str>,
}

fn main() {
    let agenda = vec![
        Contact { nume: "Ana", telefon: Some("0722 111 222") },
        Contact { nume: "Mihai", telefon: None },
        Contact { nume: "Ioana", telefon: Some("0744 333 444") },
    ];
    let de_sunat: Vec<String> = agenda
        .iter()
        .filter_map(|c| c.telefon.map(|t| format!("{}: {t}", c.nume)))
        .collect();
    println!("{:#?}", de_sunat);
}

Rezultat

[
    "Ana: 0722 111 222",
    "Ioana: 0744 333 444",
]

Exemplul 3: Valorile dintr-un fișier de configurare

Liniile fără = (comentarii, linii goale) dau None la split_once și sunt sărite.

fn main() {
    let config = "# setări\nport=8080\n\nhost = localhost\ndebug=true";
    let perechi: Vec<(&str, &str)> = config
        .lines()
        .filter_map(|linie| linie.split_once('='))
        .map(|(k, v)| (k.trim(), v.trim()))
        .collect();
    println!("{:?}", perechi);
}

Rezultat

[("port", "8080"), ("host", "localhost"), ("debug", "true")]

Greșeli frecvente

  • Scrii .map(|s| s.parse::<u32>()).filter(|r| r.is_ok()).map(|r| r.unwrap()). filter_map(|s| s.parse().ok()) face același lucru.
  • Folosești filter_map când vrei să oprești totul la prima valoare greșită. filter_map sare peste erori în tăcere; pentru oprire, colectează în Result<Vec<_>, _>.

Comenzi înrudite

  • Iterator::map: Transformă fiecare valoare a iteratorului cu o funcție și dă un iterator cu rezultatele; nu calculează nimic până la consumare.
  • Iterator::filter: Păstrează doar elementele pentru care condiția dată returnează true; restul sunt sărite.
  • Iterator::find_map: Aplică o funcție care returnează Option pe fiecare element și dă primul rezultat Some; se oprește acolo.
  • Iterator::flat_map: flatten „turtește” un iterator de colecții într-un singur șir de valori; flat_map aplică întâi o funcție, apoi turtește.
  • Iterator::map_while: Transformă elementele cât timp funcția returnează Some și se oprește la primul None.