ProgrammationDéveloppeur Rust

Comment les traits sont-ils réalisés en Rust et qu'est-ce que la dispatch dynamique et statique (dispatch dynamique/statique)? Quand faut-il utiliser chaque approche?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse

Les traits en Rust sont un moyen de définir une interface : ils décrivent un ensemble de méthodes qui doivent être implémentées par un type. L'implémentation d'un trait permet d'utiliser des génériques et le dispatch dynamique des méthodes.

  • Dispatch statique (par le biais de génériques et de bounds): Une implémentation spécifique d'une méthode est appelée au moment de la compilation. Un code générique est utilisé (impl<T: Trait> ou fn foo<T: Trait>(t: T)), une version distincte de la fonction est créée pour chaque type (monomorphisation).

  • Dispatch dynamique : Utilisé lorsque la fonction prend des objets comme références à un trait (&dyn Trait), le compilateur ne sait pas à l'avance quel type sera utilisé. L'appel de la méthode se fait via une vtable (table virtuelle) à l'exécution.

Exemple:

trait Animal { fn speak(&self); } struct Dog; impl Animal for Dog { fn speak(&self) { println!("Woof!"); } } // Dispatch statique def print_animal_static<T: Animal>(a: T) { a.speak(); } // Dispatch dynamique def print_animal_dyn(a: &dyn Animal) { a.speak(); }

Quand utiliser :

  • Statique — lorsque la performance est importante et que les types sont connus à la compilation.
  • Dynamique — lorsque vous devez travailler avec une collection hétérogène d'objets ou lorsque le type n'est pas connu à l'avance.

Question piège

Peut-on stocker des valeurs avec un type générique trait dans Vec<Trait>? Pourquoi?

Réponse : Non, ce n'est pas possible. Vec exige des types biaisés, et les traits n'ont pas de taille propre. Il faut utiliser Box<dyn Trait> ou des références aux traits.

// let v: Vec<Trait> = vec![]; // erreur de compilation let v: Vec<Box<dyn Animal>> = vec![Box::new(Dog)]; // correct

Exemples d'erreurs réelles dues à l'ignorance des subtilités du sujet


Histoire

Tentative de créer une collection d'objets trait sans utiliser Box ou Rc :

// let coll: Vec<MyTrait> = vec![]; // erreur : la taille pour les valeurs de type ... ne peut pas être connue

En conséquence — refactoring complet du code et leçon importante sur la nécessité d'utiliser Box pour les traits dynamiques.

Histoire

Un développeur junior a implémenté une API complexe via dispatch dynamique, alors qu'un dispatch statique aurait pu être approprié. Cela a conduit à une perte de performance notable et à des problèmes d'inlining, bien que tout le polymorphisme ait été superflu.

Histoire

Dans une démo client, ils ont essayé de stocker des références à des objets temporaires (&dyn Trait) pour interagir avec différents protocoles, menant à des références pendantes et des panic à l'exécution. Il a fallu passer à Box<dyn Trait> avec propriété explicite.