Zero-cost abstractions — ключевая идея Rust, означающая, что абстракции (например, generic-типы, итераторы, traits) не должны вносить издержек по времени или памяти по сравнению с ручным написанием аналогичного кода. То есть, высокоуровневый код после оптимизации компилируется так же эффективно, как и низкоуровневый.
Rust достигает этого благодаря механизму мономорфизации: generic-код компилируется для каждого конкретного типа отдельно, без динамических вызовов. Итераторы и замыкания, написанные на traits/generics, после оптимизации раскрываются компилятором в прямой жестко типизированный код, где убраны все лишние обёртки.
Пример zero-cost итератора:
let v = vec![1,2,3]; let sum: i32 = v.iter().map(|x| x * 2).sum();
Этот фрагмент после оптимизации превращается компилятором почти в ручной цикл:
let mut sum = 0; for x in &v { sum += x * 2; }
Означает ли zero-cost abstraction в Rust, что при использовании trait-объектов (например, &dyn Trait) не будет runtime overhead?
Ответ: Нет! Runtime-overhead появляется при динамическом выборе метода через vtable — когда используется dyn Trait вместо generic-функций. Zero-cost получаетcя только при статических (monomorphized) generic абстракциях.
Пример:
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(); } // Первый вызов мономорфизируется, второй — через vtable
История
История
История
В сторонней библиотеке создали generic-структуру, которую возвращали как Box<dyn Trait>. Несмотря на generic-реализацию, потеряли zero-cost, т.к. вынесли абстракцию в runtime.