Callbacks en Rust : de la douleur à l'élégance grâce aux idiomes

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Introduction : pourquoi les callbacks en Rust sont un défi (et une opportunité)

\n

Si vous avez déjà programmé en JavaScript, Python ou C#, les callbacks sont pour vous un outil familier. Vous transmettez une fonction qui sera appelée plus tard, et vous vivez sereinement. Mais quand vous arrivez en Rust, ce simple patron se transforme soudainement en champ de mines : possession, emprunt, durées de vie, dyn vs impl...

\n

Sur Stack Overflow, la question "Idiomatic callbacks in Rust" a rassemblé des milliers de vues. Cela signifie que même les développeurs expérimentés trébuchent sur ce sujet. Mais la bonne nouvelle : Rust propose non pas une, mais plusieurs manières élégantes d'implémenter des callbacks. Et aujourd'hui, nous allons toutes les analyser — des closures simples aux puissants objets-trait et patrons enum.

\n

Cet article est pour vous si :

\n
    \n
  • Vous commencez à apprendre Rust et voulez comprendre comment organiser correctement les architectures asynchrones et événementielles ;
  • \n
  • Vous passez d'autres langages et cherchez des analogues idiomatiques aux patrons familiers ;
  • \n
  • Vous voulez écrire du code Rust propre, sûr et performant.
  • \n
\n

C'est parti !

\n\n

Le problème : qu'est-ce qui ne va pas avec les callbacks simples ?

\n

Imaginez que nous écrivons un simple planificateur de tâches. En Python, cela ressemblerait à ceci :

\n
def schedule(callback):\n    # faisons quelque chose\n    callback(result)
\n

En Rust, la première tentative pourrait être :

\n
fn schedule(callback: fn(i32) -> i32) {\n    let result = 42;\n    callback(result);\n}
\n

Et cela fonctionne ! Mais seulement pour les fonctions, pas pour les closures. Essayez de passer une closure qui capture une variable :

\n
let factor = 2;\nlet closure = |x| x * factor;\nschedule(closure); // Erreur ! expected fn pointer, found closure
\n

Le compilateur se plaint à juste titre : fn est un pointeur de fonction, tandis qu'une closure est un type anonyme qui peut capturer l'environnement. Nous avons besoin d'une approche plus flexible.

\n\n

Approche 1 : Closures avec impl Fn (simplicité et flexibilité)

\n

La manière la plus simple et la plus couramment utilisée est d'utiliser un type générique avec le trait Fn, FnMut ou FnOnce.

\n

Quand utiliser Fn, FnMut, FnOnce ?

\n
    \n
  • FnOnce — si le callback est appelé exactement une fois (consomme les valeurs capturées) ;
  • \n
  • FnMut — si le callback peut modifier l'état capturé (appelé plusieurs fois) ;
  • \n
  • Fn — si le callback ne modifie pas l'état (fonction pure).
  • \n
\n

Exemple avec impl Fn :

\n
fn schedule<F>(callback: F)\nwhere\n    F: Fn(i32) -> i32,\n{\n    let result = 42;\n    println!("Résultat : {}", callback(result));\n}\n\nfn main() {\n    let factor = 2;\n    let closure = |x| x * factor;\n    schedule(closure); // Ça marche !\n    \n    // On peut aussi passer une fonction normale\n    fn double(x: i32) -> i32 { x * 2 }\n    schedule(double);\n}
\n

Avantages : extrêmement flexible, prend en charge les closures, sûr, lisible.
\nInconvénients : monomorphisation — pour chaque type de closure, le compilateur génère une version distincte de la fonction. Si les callbacks sont nombreux, le code peut gonfler.

\n\n

Approche 2 : Objets-trait Box<dyn Fn> (dispatch dynamique)

\n

Si vous voulez stocker des callbacks dans des collections (par exemple, dans un Vec) ou les passer entre threads, les génériques ne conviennent pas — un seul type est nécessaire. Les objets-trait viennent à la rescousse :

\n
type Callback = Box<dyn Fn(i32) -> i32>;\n\nstruct Scheduler {\n    callbacks: Vec<Callback>,\n}\n\nimpl Scheduler {\n    fn new() -> Self {\n        Scheduler { callbacks: Vec::new() }\n    }\n    \n    fn register(&mut self, callback                

Blogs

Book Recommendations