Rust è noto per il suo approccio alla programmazione sicura e ad alte prestazioni, ma supporta anche molti principi della programmazione funzionale. Le closure consentono di creare funzioni anonime flessibili, mentre metodi come map
, filter
e fold
permettono di trasformare dati in modo dichiarativo. Questo articolo esplora come utilizzare queste funzionalità in Rust per scrivere codice conciso ed espressivo.
Le closure sono funzioni anonime che possono catturare variabili dall'ambiente circostante. In Rust, sono definite con il simbolo |
e possono essere assegnate a variabili, passate come argomenti o utilizzate inline.
fn main() {
let quadrato = |x: i32| x * x;
println!("Il quadrato di 4 è {}", quadrato(4));
}
Qui, quadrato
è una closure che accetta un numero intero e restituisce il suo quadrato.
Le closure possono catturare variabili dal loro contesto.
fn main() {
let moltiplicatore = 2;
let moltiplica = |x: i32| x * moltiplicatore;
println!("2 x 5 = {}", moltiplica(5));
}
In questo esempio, la closure moltiplica
cattura il valore di moltiplicatore
dall'ambiente.
Rust può dedurre i tipi delle closure, ma puoi anche specificarli manualmente.
let add = |x: i32, y: i32| -> i32 { x + y };
Le funzioni di ordine superiore sono funzioni che accettano altre funzioni come argomenti o restituiscono funzioni. Rust supporta questo paradigma in modo naturale.
fn applica_funzione(f: F, x: i32) -> i32
where
F: Fn(i32) -> i32,
{
f(x)
}
fn main() {
let quadrato = |x: i32| x * x;
println!("Risultato: {}", applica_funzione(quadrato, 3));
}
La funzione applica_funzione
accetta una closure f
e un numero x
, applicando la closure al numero.
map
, filter
e fold
Rust fornisce metodi funzionali per iterare e trasformare collezioni in modo dichiarativo.
map
: Trasformare ElementiIl metodo map
applica una funzione a ogni elemento di una collezione, restituendo una nuova collezione con i risultati.
fn main() {
let numeri = vec![1, 2, 3, 4, 5];
let doppi = numeri.iter().map(|x| x * 2).collect::>();
println!("Numeri doppi: {:?}", doppi);
}
filter
: Filtrare Elementifilter
seleziona solo gli elementi che soddisfano una determinata condizione.
fn main() {
let numeri = vec![1, 2, 3, 4, 5];
let pari = numeri.iter().filter(|&&x| x % 2 == 0).collect::>();
println!("Numeri pari: {:?}", pari);
}
fold
: Ridurre una Collezionefold
riduce una collezione a un unico valore, combinando gli elementi secondo una funzione.
fn main() {
let numeri = vec![1, 2, 3, 4, 5];
let somma = numeri.iter().fold(0, |acc, x| acc + x);
println!("Somma: {}", somma);
}
Moltiplicare Valori di un Vettore
Scrivi una funzione che accetta un vettore di numeri e restituisce un nuovo vettore con i valori moltiplicati per 3 utilizzando map
.
Soluzione:
fn moltiplica(vettore: Vec) -> Vec {
vettore.into_iter().map(|x| x * 3).collect()
}
fn main() {
let numeri = vec![1, 2, 3];
println!("{:?}", moltiplica(numeri));
}
Filtrare Numeri Negativi
Scrivi una funzione che rimuove i numeri negativi da un vettore utilizzando filter
.
Soluzione:
fn filtra_positivi(vettore: Vec) -> Vec {
vettore.into_iter().filter(|&x| x >= 0).collect()
}
fn main() {
let numeri = vec![-1, 0, 1, 2, -3];
println!("{:?}", filtra_positivi(numeri));
}
Prodotto di Tutti i Numeri
Utilizza fold
per calcolare il prodotto di tutti i numeri in un vettore.
Soluzione:
fn prodotto(vettore: Vec) -> i32 {
vettore.into_iter().fold(1, |acc, x| acc * x)
}
fn main() {
let numeri = vec![1, 2, 3, 4];
println!("Prodotto: {}", prodotto(numeri));
}
Rust combina sicurezza e performance con caratteristiche della programmazione funzionale, permettendo di scrivere codice conciso e leggibile. Le closure, le funzioni di ordine superiore e metodi funzionali come map
, filter
e fold
ti offrono strumenti potenti per manipolare i dati. Nel prossimo articolo vedremo le Concurrency in Rust, quindi come creare e gestire thread, comunicare in sicurezza con i canali e utilizzare async/await per la programmazione asincrona.