Zero-cost abstracties zijn een sleutelidee in Rust, wat betekent dat abstracties (bijvoorbeeld generieke types, iterators, traits) geen tijd- of geheugenlasten mogen met zich meebrengen in vergelijking met handmatige code. Met andere woorden, hoog-niveau code compileert na optimalisatie even efficiënt als laag-niveau code.
Rust bereikt dit door middel van monomorfisering: generieke code wordt afzonderlijk gecompileerd voor elk specifiek type, zonder dynamische aanroepen. Iterators en closures, geschreven met traits/generics, worden na optimalisatie door de compiler uitgepeld naar directe, sterk getypeerde code, waarbij alle onnodige wrappers zijn verwijderd.
Voorbeeld van een zero-cost iterator:
let v = vec![1,2,3]; let sum: i32 = v.iter().map(|x| x * 2).sum();
Dit fragment wordt na optimalisatie door de compiler bijna omgezet naar een handmatige lus:
let mut sum = 0; for x in &v { sum += x * 2; }
Betekent zero-cost abstractie in Rust dat er geen runtime overhead zal zijn bij het gebruik van trait-objecten (bijvoorbeeld &dyn Trait)?
Antwoord: Nee! Runtime overhead ontstaat bij dynamische methodekeuze via vtable — wanneer dyn Trait wordt gebruikt in plaats van generieke functies. Zero-cost wordt alleen verkregen met statische (monomorfized) generieke abstracties.
Voorbeeld:
trait Speaker { fn speak(&self); } fn say_twice<T: Speaker>(v: T) { v.speak(); v.speak(); } fn say_twice_dyn(v: &dyn Speaker) { v.speak(); v.speak(); } // De eerste aanroep wordt gemonomorfiseerd, de tweede — via vtable
Verhaal
Verhaal
Verhaal
In een externe bibliotheek maakten ze een generieke structuur die werd teruggegeven als Box<dyn Trait>. Ondanks de generieke implementatie verloren ze zero-cost, omdat ze de abstractie naar runtime verhuisden.