Le dispatch est un mécanisme de sélection d'une fonction (méthode) spécifique à appeler. En Rust, il existe deux approches : le dispatch statique et le dispatch dynamique.
Contexte :
Dans les langages orientés objet, un appel de méthode dynamique utilise généralement une vtable (table virtuelle). En Rust, un mécanisme similaire est mis en œuvre pour les trait objects — des références à des objets de types implémentant certains traits. Le dispatch statique apparaît lors de l'utilisation de génériques et de limites de traits.
Problème :
Il faut souvent choisir entre la flexibilité (la capacité à travailler avec des objets de types différents via une interface unique) et la performance (le dispatch statique permet d'inliner des méthodes). Un choix incorrect conduit soit à des génériques excessivement compliqués, soit à des pertes de performance.
Solution :
Le dispatch statique est atteint via des paramètres génériques : dans ce cas, le compilateur génère un code distinct pour chaque type. Le dispatch dynamique se produit si une fonction prend un argument de type &dyn Trait ou Box<dyn Trait>, alors lors de l'appel de méthode sur le trait, Rust consulte la vtable à l'adresse, comme dans les langages OOP classiques.
Exemple de code :
trait Shape { fn area(&self) -> f64; } impl Shape for Circle { fn area(&self) -> f64 { 3.1415 * self.radius * self.radius } } fn print_area(shape: &dyn Shape) { // dispatch dynamique println!("area = {}", shape.area()); } // Ou statiquement : fn print_area_static<S: Shape>(shape: &S) { println!("area = {}", shape.area()); }
Caractéristiques clés :
dyn Trait utilise vtable (dispatch dynamique)Peut-on faire Box<dyn Sized>?
Non. dyn Trait est par définition — unsized, il nécessite toujours l'utilisation de Box, Arc ou des références, mais pas «Box<dyn Sized>» — cela n'a pas de sens. Les trait objects n'ont pas le trait Sized.
Dyn Trait est-il autorisé pour les traits avec des méthodes génériques ?
Non. Il n'est pas possible de créer des traits object-safe avec des méthodes génériques (souvent confondu !) ; les types composites ne sont pas safety-object :
trait MyTrait { fn foo<T>(&self, x: T); } let x: &dyn MyTrait = ... // Erreur de compilation !
Peut-on faire dyn Trait pour un trait avec des valeurs Self dans la signature ?
Non, si la méthode renvoie Self (beaucoup de gens ne comprennent pas ce point : la sécurité des objets exige qu'il n'y ait pas de Self dans la signature ; on peut utiliser self uniquement dans les arguments, mais pas dans le retour).
Partout, nous avons utilisé dyn Trait pour l'universalité des interfaces, même à l'intérieur de boucles serrées, où les génériques auraient suffi.
Avantages :
Inconvénients :
Le dispatch statique a été utilisé dans la logique interne, tandis que dyn Trait était utilisé uniquement aux frontières des modules.
Avantages :
Inconvénients :