Zero-Cost Abstractions: Myth or Reality?
“Zero-cost” does not mean free. It means you do not pay for what you do not use, and what you do use should compile down to the code you would have written by hand.
That claim is true often enough to be useful, and false often enough to be dangerous if you never look at the assembly.
Where it holds
A tight iterator chain over a slice usually collapses to the same loop as a for i in 0..n in --release. Generics monomorphize. Option<&T> is a nullable pointer. Those are the examples that made the slogan famous.
pub fn sum_even(xs: &[u32]) -> u32 {
xs.iter().copied().filter(|x| x % 2 == 0).sum()
}On a current LLVM, that is a vectorizable loop. The adapters are a compile-time story, not a runtime one.
Where it leaks
The cost shows up when the abstraction hides:
- Indirect calls (
dyn Trait) that the compiler cannot devirtualize - Extra copies because a type is not
Copyand you.clone()to satisfy a bound - Debug formatting, logging, or panic paths that survive into the hot function
Zero-cost is a property of a specific compilation, not of the language in the abstract.
How to check
Keep a cargo asm or Compiler Explorer tab next to the function you care about. If the iterator version and the hand-written version diverge, the slogan just told you where to look.