Zero-Cost-Abstraktionen sind eine Schlüsselidee in Rust, die besagt, dass Abstraktionen (wie generische Typen, Iteratoren, Traits) keine Zeit- oder Speicherkosten im Vergleich zum manuellen Schreiben ähnlichen Codes verursachen sollten. Das bedeutet, dass hochgradiger Code nach der Optimierung ebenso effizient kompiliert wird wie niedergradiger Code.
Rust erreicht dies durch den Mechanismus der Monomorphisierung: generischer Code wird für jeden spezifischen Typ separat kompiliert, ohne dynamische Aufrufe. Iteratoren und Closures, die mit Traits/Generics geschrieben sind, werden nach der Optimierung vom Compiler in direkten, stark typisierten Code umgewandelt, wobei alle überflüssigen Wrapper entfernt werden.
Beispiel für einen Zero-Cost-Iterator:
let v = vec![1,2,3]; let sum: i32 = v.iter().map(|x| x * 2).sum();
Dieses Fragment wird nach der Optimierung vom Compiler fast in eine manuelle Schleife umgewandelt:
let mut sum = 0; for x in &v { sum += x * 2; }
Bedeutet Zero-Cost-Abstraktion in Rust, dass es bei der Verwendung von Trait-Objekten (z. B. &dyn Trait) keine Laufzeit-Kosten gibt?
Antwort: Nein! Laufzeitkosten entstehen bei der dynamischen Methodenwahl über die vtable — wenn dyn Trait anstelle von generischen Funktionen verwendet wird. Zero-Cost ergibt sich nur aus statischen (monomorphisierten) generischen Abstraktionen.
Beispiel:
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(); } // Der erste Aufruf wird monomorphisiert, der zweite - über die vtable
Geschichte
Geschichte
Geschichte
In einer externen Bibliothek wurde eine generische Struktur erstellt, die als Box<dyn Trait> zurückgegeben wurde. Trotz der generischen Implementierung verlor man den Zero-Cost, da die Abstraktion zur Laufzeit hervorgehoben wurde.