ProgrammationDéveloppeur Rust

Comment fonctionnent les closures en Rust ? Quels types de closures existent, en quoi diffèrent-elles et quand sont-elles utilisées ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse

En Rust, les closures sont des fonctions anonymes qui peuvent "capturer" des variables de l'extérieur de leur portée. La syntaxe :

let add = |a: i32, b: i32| a + b;

Il existe trois types de closures en Rust (qui diffèrent par la méthode de capture des variables) :

  • Fn : capture des références (&T), peut être appelée plusieurs fois sans modifier l'environnement.
  • FnMut : capture des références avec possibilité de modification (&mut T), peut modifier les données de la closure lors de l'appel.
  • FnOnce : ne peut être appelée qu'une seule fois, car elle prend possession des données capturées (T).

Rust détermine automatiquement le type nécessaire, mais vous pouvez le spécifier explicitement.

Exemple de différences :

let s = String::from("hello"); // FnOnce let consume = move || println!("{}", s); // s n'est plus accessible après l'appel

Question piège

Pourquoi une closure avec le mot-clé move peut-elle ne pas être FnOnce, mais plutôt être Fn ou FnMut ?

Réponse : Le mot-clé move effectue la capture des variables par possession, mais si les données à l'intérieur de la closure ne sont pas modifiées ou détruites, la closure reste compatible avec Fn/FnMut. Par exemple :

let s = String::from("abc"); let c = move || println!("String: {}", s); // c: impl Fn() c();

c peut être appelée plusieurs fois : la valeur de s est copiée dans la closure, mais n'est pas détruite.

Exemples d'erreurs réelles dues à une méconnaissance des subtilités du sujet


Histoire

Un développeur Rust débutant dans un projet avec des flux de données a essayé d'écrire une closure sans move, en la passant à un autre flux. En conséquence, le compilateur se plaignait d'un outlived borrow, et il a dû se plonger dans les subtilités des lifetimes et du move.


Histoire

Ils ont introduit de la mutabilité à l'intérieur de la closure, sans changer le type en FnMut, ce qui a causé des erreurs "mismatched types" à tous les endroits où ils utilisaient des itérateurs.


Histoire

Lors du traitement d'un grand tableau, la closure a pris possession par move de toute la collection, déplaçant involontairement la propriété, ce qui a conduit le code extérieur à perdre l'accès aux données après la première itération, entraînant des tentatives d'accès à des valeurs déplacées.