I am a beginner for Rust and have some experience with C and Python. I just cannot figure out what "zero cost abstraction " is for Rust. Anyone can explain it for me?
@johnthagen Thanks for the helpful link. And is there any example shows the non zero cost abstraction for other language, like C++?
“zero cost abstractions” is also for me the possibility to use high level concepts, like a functional programming approach, without any performance costs.
For example in Python
sum(range(1000)) actually does 1000 iterations and 1000 additions, so there’s a cost to writing it that way.
OTOH in Rust
(0..1000).sum() compiles down to the constant
499500, so it costs nothing to execute.
Similar optimizations are also possible in other compiled languages, so Rust isn’t entirely unique in having zero-cost abstractions like this.
The most notable difference is that Rust can track memory using lifetimes (zero cost at runtime) instead of reference counting or GC. In C++ you either don’t get the abstraction and manage moving and freeing yourself, or you pay the cost of some form of GC.
Worth mentioning that it’s not just about instruction-level optimizations, but also memory layout/representation. For instance, JVMs can do a lot of the same optimizations that GCC/Clang do for C/C++ code, but (current) Java will make you pay in memory and indirection if you want to wrap values in custom types. So a Java-equivalent of
struct CustomInt(i32) is going to incur mem bloat and indirection.
It means that sometimes in rust it is possible to have easy/higher level/abstracted code that is just as fast at run time as the hard/low level/finicky code it is wrapping.
Diesel the orm is just as fast or faster than rust-postgres which uses raw sql.
In python: Every layer of abstraction is slower than the last. Sqlalchemy is slower than the drivers it wraps, and forgoes a lot of normal python idioms internally to get as fast as it is.
In general, “zero cost abstractions” means the abstractions are just as performant as if you had written the underlying code by hand (e.g. iterators just as fast as manual loops, generics just as fast as if you had hand-written specialized types, closures just as fast as if you had written a manual function, wrapping 2 i32s in a struct is no more expensive than using those 2 i32s individually, etc). A lot of the “zero cost” comes from and relies on sophisticated compilers, but language semantics/features play a role too.
It is worth mentioning that the term zero cost abstraction actually comes from C++ (at least I think so, it might have been used even earlier). For example, templates in C++ compiles to different versions of the function for different types, incuring zero runtime cost for the generality. Other abstractions in C++, such as virtual methods, has non-zero runtime cost.
Rust, just like C++, has some zero cost abstractions and some abstraction that has a runtime cost. It just has some more of the zero-cost kind (as examplified by others in this thread).
I don’t think a zero cost abstraction implies no runtime cost whatsoever. Like @vitalyd said, it’s more of a comparison to the cost of doing the same thing by hand, without the abstraction. So for things like C++ virtual methods, it’s still a zero cost abstraction, because the alternative is for you to store a function pointer in your struct and call it – exactly like vtables.
Sometimes the only alternative has similar cost (as in virtual functions vs function pointers). But in lots of cases, the indirection really is the cost of the abstraction. If you write a method to sort integers, you can use single inline machine instructions for the comparisions, but if you want to sort arbitrary objects, you need to use a function pointer for the comparisions.
Then along comes c++ templates and rust generics, allowing you to write a function that can sort arbitrary objects and then have the compiler monomorphize the method and again inline the sorting instructions (in the cases it is short enough for inlining to be reasonable).
In many languages (such as java and python) part of the abstraction cost for classes / structs is that all methods will be virtual whether you need them to or not.
JVMs (at least Oracle Hotspot) devirtualize pretty aggressively at JIT time, based on profile information. But yeah, it’s not guaranteed and can fail to happen under certain circumstances. I’d say Java’s biggest fail at the moment is no value types/structs which leads to horrible bloat and rampant indirection.