Rust

Any donation is very welcome
Fork me on GitHub

II. Spécificités de Rust

9. Closure

Nous allons maintenant aborder un chapitre très important pour le langage Rust. Ceux ayant déjà utilisé des langages fonctionnels n'y verront qu'une révision dans ce chapitre (mais ça ne fait jamais de mal après tout !).

Pour ceux qui n'ont jamais utilisé de closures, c'est une fonction anonyme qui capture son environnement.

"Une fonction "anonyme" ? Elle "capture" son environnement ?"

Ne vous inquiétez pas, vous allez très vite comprendre, prenons un exemple simple :

Runlet multiplication = |nombre: i32, multiplicateur| nombre * multiplicateur;

println!("{}", multiplication(2, 2));

Pour le moment, vous vous dites sans doute qu'en fait, ce n'est qu'une fonction. Maintenant ajoutons un élément :

Runlet nombre = 2i32;
let multiplication = |multiplicateur| nombre * multiplicateur;

println!("{}", multiplication(2));

Là je pense que vous vous demandez comment il fait pour trouver la variable nombre puisqu'elle n'est pas dans le scope de la "fonction". Comme je vous l'ai dit, une closure capture son environnement, elle a donc accès à toutes les variables présentes dans le scope de la fonction qui l'appelle.

Mais à quoi ça peut bien servir ? Imaginons que vous avez une super interface graphique et que vous voulez effectuer une action lors d'un événement, disons lorsque le bouton est cliqué. Hé bien cela donnerait quelque chose dans ce genre :

Runlet mut bouton = Bouton::new();
let mut clicked = false;

bouton.clicked(|titre| {
    clicked = true;
    println!("On a cliqué sur le bouton {} !", titre);
});

Facile, non ? Cela dit, vous pourriez aussi vous servir des closures pour trier un vecteur d'objets ou d'autres choses similaires.

Si jamais vous souhaitez écrire une fonction recevant une closure en paramètre, voici à quoi cela va ressembler :

Runfn fonction_avec_closure<F>(closure: F) -> i32
    where F: Fn(i32) -> i32 {
    closure(1)
}

Ici, la closure prend un i32 comme paramètre et renvoie un i32. Vous remarquerez que la syntaxe est proche de celle d'une fonction générique, la seule différence venant du mot-clé where qui permet de définir à quoi doit ressembler la closure. À noter qu'on aurait aussi pu écrire la fonction de cette façon :

Runfn fonction_avec_closure<F: Fn(i32) -> i32>(closure: F) -> i32 {
    closure(1)
}

Nous avons donc vu les bases des closures. C'est une partie importantes, je vous conseille donc de bien vous entraîner dessus jusqu'à être sûr de bien les maîtriser !

Après ça, il est temps d'attaquer un chapitre un peu plus "tranquille".