Las zero-cost abstractions son la idea clave de Rust, lo que significa que las abstracciones (por ejemplo, tipos genéricos, iteradores, traits) no deben implicar costos en tiempo o memoria en comparación con la escritura manual de código equivalente. Es decir, el código de alto nivel se compila de manera tan eficiente como el código de bajo nivel después de la optimización.
Rust logra esto gracias al mecanismo de monomorfización: el código genérico se compila para cada tipo concreto por separado, sin llamadas dinámicas. Los iteradores y cierres escritos sobre traits/generics son desglosados por el compilador en código estáticamente tipado directo, donde se eliminan todos los envoltorios innecesarios.
Ejemplo de un iterador de cero costo:
let v = vec![1,2,3]; let sum: i32 = v.iter().map(|x| x * 2).sum();
Este fragmento se convierte después de la optimización en un ciclo manual casi equivalente:
let mut sum = 0; for x in &v { sum += x * 2; }
¿Significa que la zero-cost abstraction en Rust implica que no habrá overhead en tiempo de ejecución al usar objetos trait (por ejemplo, &dyn Trait)?
Respuesta: ¡No! El overhead en tiempo de ejecución aparece al seleccionar dinámicamente un método a través de vtable — cuando se usa dyn Trait en lugar de funciones genéricas. La zero-cost solo se obtiene con abstracciones genéricas estáticas (monomorfizadas).
Ejemplo:
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(); } // La primera llamada se monomorfiza, la segunda — a través de vtable
Historia
Historia
Historia
En una biblioteca externa, se creó una estructura genérica que se devolvió como Box<dyn Trait>. A pesar de la implementación genérica, se perdió zero-cost porque se llevó la abstracción a tiempo de ejecución.