ProgrammatieRust ontwikkelaar

Hoe zijn traits geïmplementeerd in Rust en wat is dynamische en statische dispatch? Wanneer moet je elke benadering gebruiken?

Slaag voor sollicitatiegesprekken met de Hintsage AI-assistent

Antwoord

Traits in Rust zijn een manier om een interface te definiëren: ze beschrijven een set methoden die door een type moeten worden geïmplementeerd. De implementatie van een trait maakt het mogelijk om generics en dynamische dispatch van methoden te gebruiken.

  • Statische dispatch (via generics en bounds): De specifieke implementatie van een methode wordt tijdens de compilatie aangeroepen. Gebruikt generieke code (impl<T: Trait> of fn foo<T: Trait>(t: T)), en voor elk type wordt er tijdens de compilatie een aparte versie van de functie gemaakt (monomorfisatie).

  • Dynamische dispatch: Wordt gebruikt wanneer de functie objecten accepteert als verwijzingen naar traits (&dyn Trait), de compiler weet van tevoren niet welk type zal worden gebruikt. De methoden worden aangeroepen via een vtable (virtuele tabel) tijdens runtime.

Voorbeeld:

trait Animal { fn speak(&self); } struct Dog; impl Animal for Dog { fn speak(&self) { println!("Woef!"); } } // Statische dispatch def print_animal_static<T: Animal>(a: T) { a.speak(); } // Dynamische dispatch def print_animal_dyn(a: &dyn Animal) { a.speak(); }

Wanneer te gebruiken:

  • Statische — wanneer prestaties belangrijk zijn en types tijdens compilatie bekend zijn.
  • Dynamische — wanneer je met een heterogene verzameling objecten moet werken of het type niet van tevoren bekend is.

Vragend met een twist

Kan je waarden met een generiek type trait opslaan in Vec<Trait>? Waarom?

Antwoord: Nee, dat kan niet. Vec vereist sized-types, en traits hebben zelf geen grootte. Je moet Box<dyn Trait> of verwijzingen naar traits gebruiken.

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

Voorbeelden van echte fouten door het gebrek aan kennis over de fijne kneepjes van het onderwerp


Verhaal

Poging om een collectie van trait-objecten te maken zonder gebruik te maken van Box of Rc:

// let coll: Vec<MyTrait> = vec![]; // fout: de grootte voor waarden van type ... kan niet bekend zijn

Het resultaat — volledige refactoring van de code en een belangrijke les over de noodzaak om Box te gebruiken voor dynamische traits.

Verhaal

Een junior ontwikkelaar implementeerde een complexe API via dynamische dispatch, waar deze statisch had kunnen zijn. Dit leidde tot een merkbare prestatieverlies en problemen met inlining, terwijl alle polymorfisme overbodig was.

Verhaal

Bij de demo klant probeerden ze verwijzingen op te slaan naar tijdelijke objecten (&dyn Trait) voor interactie met verschillende protocollen, wat leidde tot dangling references en runtime panic. Ze moesten overschakelen naar Box<dyn Trait> met expliciet eigenaarschap.