f in x
Iteratori e Closure in Rust — Performance Funzionale Senza Overhead di Runtime
> cd .. / HUB_EDITORIALE > Visualizza in Inglese
Sviluppo di siti web

Iteratori e Closure in Rust — Performance Funzionale Senza Overhead di Runtime

[2026-07-11] Author: Ing. Calogero Bono
Zenithby Meteora Web Il sistema operativo della tua attività. Social, clienti, prenotazioni e fatture in un'unica piattaforma. Palestre, barber, professionisti. Scopri Zenith Demo gratis · senza carta

Hai mai scritto un for in Rust e pensato: “c’è un modo più elegante e veloce?” Poi hai scoperto gli iteratori e le closure, ma ti sei chiesto se non siano solo zucchero sintattico che rallenta il codice. Noi di Meteora Web lavoriamo ogni giorno su sistemi ad alte prestazioni — server HTTP, elaborazione dati, tool da riga di comando — e la risposta è netta: gli iteratori in Rust sono spesso più veloci dei loop manuali, grazie alle zero-cost abstractions. Ecco come usarli senza paura.

Perché gli iteratori in Rust sono più efficienti dei loop scritti a mano?

Il mantra di Rust è “zero-cost abstraction”: ciò che scrivi con iteratori e closure viene compilato nello stesso codice macchina di un loop ottimizzato a mano. I metodi come map, filter, fold non allocano collezioni intermedie — sono lazy. Ogni passo consuma un elemento alla volta, e il compilatore fonde le catene in un unico ciclo senza overhead.

Esempio concreto: prendiamo un vettore di interi, filtriamo i pari, li moltiplichiamo per 2 e sommiamo il risultato.

// Versione con loop manuale
fn somma_pari_raddoppiati_loop(vec: &[i32]) -> i32 {
    let mut somma = 0;
    for &x in vec {
        if x % 2 == 0 {
            somma += x * 2;
        }
    }
    somma
}

// Versione con iteratori e closure
fn somma_pari_raddoppiati_iter(vec: &[i32]) -> i32 {
    vec.iter()
        .filter(|&&x| x % 2 == 0)
        .map(|&x| x * 2)
        .sum()
}

assert_eq!(somma_pari_raddoppiati_loop(&[1,2,3,4]), 12);
assert_eq!(somma_pari_raddoppiati_iter(&[1,2,3,4]), 12);

Il compilatore genera assembly identico per le due funzioni. Zero overhead. E il codice iteratore è più leggibile, privo di variabili mutabili esplicite e più facile da mantenere.

Sponsored Protocol

Come usare le closure per trasformare i dati senza allocazioni inutili?

Le closure in Rust possono catturare variabili dall’ambito circostante per riferimento immutabile (Fn), riferimento mutabile (FnMut) o per valore (FnOnce). La scelta giusta evita copie inutili e rispetta le regole di ownership.

Scenario reale: abbiamo una lista di utenti e vogliamo estrarre i nomi di quelli attivi.

#[derive(Debug)]
struct User {
    name: String,
    active: bool,
}

fn nomi_attivi(users: &[User]) -> Vec<String> {
    users.iter()
        .filter(|u| u.active)                  // closure Fn che cattura per rif. immutabile
        .map(|u| u.name.clone())               // serve clone perché vogliamo possedere il nome
        .collect()
}

Se non ci serve il nome posseduto, possiamo restituire un riferimento: .map(|u| &u.name) e cambiare il tipo di ritorno in Vec<&String>. Zero allocazioni extra.

Sponsored Protocol

Per operazioni più complesse — ad esempio, aggregare dati con stato — usiamo fold:

let numeri = [1, 2, 3, 4];
let somma = numeri.iter().fold(0, |acc, &x| acc + x);
assert_eq!(somma, 10);

Quali sono le best practice per combinare iteratori e closure in codice di produzione?

Dopo oltre 8 anni di progetti Rust (e molto prima con altri linguaggi funzionali), abbiamo imparato che la differenza tra codice elegante e codice lento spesso sta in dettagli sottili.

Scegli la forma giusta di iteratore

  • iter() -> restituisce riferimenti immutabili (&T).
  • iter_mut() -> riferimenti mutabili (&mut T).
  • into_iter() -> consuma la collezione e restituisce valori posseduti (T).

Usare into_iter() quando non ti serve più la collezione originale evita cloni inutili.

Catena di adattatori: prediligi i lazy

map, filter, take, skip, flatten sono tutti lazy. Non eseguono nulla finché non chiami un consumatore come collect(), sum(), for_each(). Sfrutta questa natura per comporre trasformazioni complesse senza allocazioni intermedie.

Sponsored Protocol

let v = vec![1, 2, 3, 4, 5];
let primi_tre_quadrati: Vec<_> = v.iter()
    .map(|x| x * x)
    .take(3)
    .collect();

Qui map viene eseguito solo sui primi tre elementi, non su tutti e cinque.

Evita collect multiple se non necessario

Ogni chiamata a collect() alloca una nuova collezione. Se devi fare più passaggi, cerca di combinarli in un’unica catena o usa fold con un accumulatore.

Come evitare errori comuni con il borrowing e le closure?

Il compilatore Rust è severo, ma i messaggi di errore a volte confondono. Ecco i due casi più frequenti che vediamo nei progetti che ci arrivano.

Closure che prova a modificare una variabile catturata per riferimento immutabile

let mut count = 0;
let numeri = vec![1, 2, 3];
let _ = numeri.iter().for_each(|_| count += 1); // ERRORE: count è catturato per rif. immutabile

Soluzione: dichiarare la closure come FnMut esplicitamente — ma attenzione, for_each accetta solo FnMut. Qui il problema è che count è mut ma la closure lo cattura per riferimento immutabile perché for_each vuole FnMut e Rust inferisce la cattura minima. Basta usare iter().for_each(|_| count += 1) funziona? No, perché count non è mut all'interno della closure? In realtà il codice funziona se count è mut e la closure lo modifica: for_each accetta FnMut e la modifica è consentita. L'errore vero è quando si tenta di prendere un riferimento mutabile a count mentre esiste già un riferimento immutabile. Esempio corretto:

Sponsored Protocol

let mut count = 0;
let numeri = vec![1, 2, 3];
numeri.iter().for_each(|_| count += 1); // OK: FnMut cattura per rif. mutabile
println!("{}", count); // 3

Piuttosto, l'errore classico è con filter che richiede Fn: se provi a modificare una variabile dentro filter, il compilatore lo blocca.

Lifetime delle closure

Quando una closure cattura riferimenti, può ereditare vincoli di lifetime. Se restituisci una closure da una funzione, devi spesso specificare i lifetime:

fn make_adder(x: &i32) -> impl Fn(i32) -> i32 + '_ {
    |y| x + y
}

L'annotazione '_ dice che la closure cattura un riferimento con lifetime anonimo.

Sponsored Protocol

Cosa fare adesso

Abbiamo smontato il mito: iteratori e closure in Rust non sono “lenti da funzionale”. Sono la via più performante e leggibile per elaborare dati, purché si seguano poche regole.

  1. Riscrivi un loop manuale del tuo progetto attuale usando iteratori e closure. Confronta il codice assembly generato con cargo asm o cargo-llvm-lines.
  2. Controlla la catena di metodi: stai usando iter(), iter_mut() o into_iter() in modo coerente? Stai allocando collezioni intermedie con collect multiple?
  3. Esercitati con le closure: prova a catturare variabili per valore (move), per riferimento mutabile e immutabile. Guarda come cambiano i trait (Fn, FnMut, FnOnce).
  4. Approfondisci con la documentazione ufficiale di Rust: Capitolo 13 del Rust Book e Modulo std::iter.
  5. Se lavori su sistemi embedded o real-time, ricorda che gli iteratori sono no_std-compatibili (con le dovute limitazioni) — non ci sono scuse.

Noi, di Meteora Web, usiamo questi pattern ogni giorno per costruire software che regge carichi reali senza sprechi. Se vuoi portare le tue competenze Rust al livello successivo, parti da qui: la nostra guida pillar su Rust.

Ing. Calogero Bono

> AUTHOR_EXTRACTED

Ing. Calogero Bono

Ingegnere informatico, fondatore di Meteora Web e Zenith OS. System administrator e progettista di piattaforme, app e CMS proprietari, con esperienza in sviluppo full-stack, marketing digitale ed ecosistema Google.
[ Read Full Dossier ]

> METEORA_WEB // WEB AGENCY

Costruiamo la presenza digitale che la tua azienda merita.

Siti web, social, pubblicità online, e-commerce e hosting performante: ingegnerizzati con metodo da ingegneri informatici a Sciacca, per tutta Italia.

> MW_JOURNAL

> READ_ALL()