Les closures et les itérateurs sont deux des fonctionnalités les plus puissantes et élégantes de Rust. Combinés, ils permettent de transformer, filtrer et collecter des données de façon expressive et performante, sans allocation intermédiaire grâce à l’évaluation paresseuse (lazy evaluation). Dans cette unité, vous apprendrez également à manipuler les chaînes de caractères, un aspect fondamental pour tout utilitaire de traitement de texte.
Les closures : fonctions anonymes
Une closure est une fonction anonyme qui peut capturer des variables de son environnement. La syntaxe utilise des pipes :
fn main() {
let multiplicateur = 3;
// Closure qui capture multiplicateur
let tripler = |x| x * multiplicateur;
println!("{}", tripler(5)); // 15
println!("{}", tripler(10)); // 30
// Closure avec type explicite
let additionner = |a: i32, b: i32| -> i32 { a + b };
println!("{}", additionner(3, 4)); // 7
}
Les traits Fn, FnMut, FnOnce
Rust distingue trois traits pour les closures selon comment elles capturent leur environnement : Fn (emprunt immuable, peut être appelée plusieurs fois), FnMut (emprunt mutable, peut modifier les captures), et FnOnce (consomme la valeur capturée, ne peut être appelée qu’une fois).
Les itérateurs : traitement paresseux
Les itérateurs en Rust utilisent une évaluation paresseuse : les transformations sont enchaînées mais n’exécutent rien tant qu’on ne les consomme pas avec collect(), for_each(), ou un accumulateur. Cela permet des chaînes de transformation sans allocation intermédiaire.
let references = vec!["DOC-2024-001", "FACT-2023-042", "DOC-2024-007", "INV-2024-003"];
// Filtrer les DOC, extraire le numéro, collecter
let numeros_doc: Vec<u32> = references.iter()
.filter(|r| r.starts_with("DOC"))
.filter_map(|r| r.split("--").last())
.filter_map(|n| n.parse::<u32>().ok())
.collect();
println!("{:?}", numeros_doc);
Les méthodes itérateurs essentielles
let nombres = vec![1, 2, 3, 4, 5, 6];
// map : transformer chaque élément
let doubles: Vec<i32> = nombres.iter().map(|&x| x * 2).collect();
// filter : garder les éléments satisfaisant un prédicat
let pairs: Vec<&i32> = nombres.iter().filter(|&&x| x % 2 == 0).collect();
// fold/reduce : accumuler
let somme: i32 = nombres.iter().sum();
let produit: i32 = nombres.iter().fold(1, |acc, &x| acc * x);
// enumerate : obtenir index + valeur
for (i, val) in nombres.iter().enumerate() {
println!("nombres[{}] = {}", i, val);
}
// zip : combiner deux itérateurs
let lettres = vec!["a", "b", "c"];
let paires: Vec<_> = nombres.iter().zip(lettres.iter()).collect();
String vs &str : la distinction fondamentale
Rust distingue deux types de chaînes : String (propriété, heap-alloué, modifiable) et &str (emprunt, slice d’une chaîne). Comprendre cette distinction est essentiel pour écrire du code idiomatique.
// &str : slice immuable, souvent des littéraux
let s1: &str = "bonjour";
// String : propriétaire, modifiable
let mut s2: String = String::from("bonjour");
s2.push_str(", monde");
s2.push('!');
// Convertir
let s3: String = s1.to_string();
let s4: &str = &s2; // deref coercion
Méthodes de manipulation de chaînes
let ref_str = " DOC-2024-042 ";
let propre = ref_str.trim();
let parties: Vec<&str> = propre.split("--").collect();
println!("{}", propre.starts_with("DOC")); // true
println!("{}", propre.contains("2024")); // true
println!("{}", propre.to_uppercase());
let reformate = propre.replace("--", "/");
let rejointe = parties.join("_");
- Les closures capturent leur environnement avec la syntaxe
|args| corps - Les itérateurs sont paresseux : chaînez les transformations, consommez avec
collect() map,filter,filter_map,foldsont les méthodes itérateurs les plus utilisées&strpour emprunter du texte,Stringpour en posséder — préférez&stren paramètre
📚 Formation : Rust : Développer des utilitaires système

-
Add a note