> Are long compile times just a product of Rust’ design? Or is there an issue with the compiler.
Actually rustc has many modern features that say C++ compilers don't. For example, clang is not an incremental compiler, nor does it do parallel codegen. Rust also has had work on multithreading support. These features were introduced all after rustc already existed. It would have been unimaginably hard to retrofit clang to this, as C++ is way harder to refactor than Rust.
So I'd put it onto the design instead of the compiler. But it's not the safety features of Rust that cause the main slowdown, at least not directly. Yes, borrow checking is some additional step rustc has to do, and NLL introduction took a major engineering project to not regress compiler performance, but overall these safety checks don't make up such a large part of the compile process.
First, Rust has larger compile units than C. If you change one file in Rust, the entire crate has to be recompiled, while in C only the single file needs recompilation (unless it's a header, then everything that imports it needs recompilation). That's also why incremental compilation is much more important for Rust than it is for C (and it increasingly becomes important for modern C++ as compile units increase).
Second, in Rust you compile everything, including your dependencies. It's not like in C or C++ where you install *-dev packages for the heavy libraries. In fact, if you compiled everything yourself in the C/C++ world, often you'd get similar compile times or even worse ones. Rust has an unstable library format. It's literally just mmap'ed internal data structures. Two different versions of the compiler can't reuse the same library, after a compiler update you have to recompile everything from scratch, and if you want to be able to add new dependencies or cargo update, you need to always use the latest compiler, which gets released quite often. So many projects that I maybe touch once every 6 months I basically have to recompile from scratch again. This is all due to design and policy questions, not because the compiler itself is slow.
Third, in Rust you statically link everything. This puts greater load onto the linker which now has to copy large amounts of data to obtain large binaries. There is no stable ABI so you can't create a dynamically linked library with a safe interface (you can of course create one with an unsafe C interface but that's not nice). There is also no cargo support for it.
Fourth, heavy use of monomorphization. A lot of libraries in Rust are generic either on lifetimes or on types. Lifetimes can be stripped, but if your code is dependent on a type it gets copied and then recompiled for every different type. This is a design question and has benefits in the final program as the code can be optimized for the type. But it has to be optimized. This incurs a compile time cost.
"Safe" and "unsafe" is quite orthogonal to the ABI stability issue. Whether a library call is "safe" has to do with on what constraints have been placed on the library code, not what ABI is used.
Rust supports a stable but unsafe ABI, the C ABI (extern "C"). Rust also supports a safe but unstable ABI, the Rust ABI (extern "Rust"). But there is no ABI that Rust supports that is both safe and stable. Full list of supported ABIs: https://doc.rust-lang.org/reference/items/external-blocks.ht...
Visual C++ to my knowledge does not support incremental or parallel compilation within the compile unit. It does support incremental linking though, and parallel compilation of multiple compile units that don't depend on each other. Features which both clang and rustc have btw (well clang only concerns one compile unit, so the parallelism depends on how you call clang, but many clang calling build systems support parallelism).
No idea about C++ Builder. Do you have links to documentation to back it up? I'm curious :).
Actually rustc has many modern features that say C++ compilers don't. For example, clang is not an incremental compiler, nor does it do parallel codegen. Rust also has had work on multithreading support. These features were introduced all after rustc already existed. It would have been unimaginably hard to retrofit clang to this, as C++ is way harder to refactor than Rust.
So I'd put it onto the design instead of the compiler. But it's not the safety features of Rust that cause the main slowdown, at least not directly. Yes, borrow checking is some additional step rustc has to do, and NLL introduction took a major engineering project to not regress compiler performance, but overall these safety checks don't make up such a large part of the compile process.
First, Rust has larger compile units than C. If you change one file in Rust, the entire crate has to be recompiled, while in C only the single file needs recompilation (unless it's a header, then everything that imports it needs recompilation). That's also why incremental compilation is much more important for Rust than it is for C (and it increasingly becomes important for modern C++ as compile units increase).
Second, in Rust you compile everything, including your dependencies. It's not like in C or C++ where you install *-dev packages for the heavy libraries. In fact, if you compiled everything yourself in the C/C++ world, often you'd get similar compile times or even worse ones. Rust has an unstable library format. It's literally just mmap'ed internal data structures. Two different versions of the compiler can't reuse the same library, after a compiler update you have to recompile everything from scratch, and if you want to be able to add new dependencies or cargo update, you need to always use the latest compiler, which gets released quite often. So many projects that I maybe touch once every 6 months I basically have to recompile from scratch again. This is all due to design and policy questions, not because the compiler itself is slow.
Third, in Rust you statically link everything. This puts greater load onto the linker which now has to copy large amounts of data to obtain large binaries. There is no stable ABI so you can't create a dynamically linked library with a safe interface (you can of course create one with an unsafe C interface but that's not nice). There is also no cargo support for it.
Fourth, heavy use of monomorphization. A lot of libraries in Rust are generic either on lifetimes or on types. Lifetimes can be stripped, but if your code is dependent on a type it gets copied and then recompiled for every different type. This is a design question and has benefits in the final program as the code can be optimized for the type. But it has to be optimized. This incurs a compile time cost.