I have been researching about Rust and it just made me curious, Rust has:
-
Pretty hard syntax.
-
Low level langauge.
-
Slowest compile time.
And yet, Rust has:
-
A huge community.
-
A lot of frameworks.
-
Widely being used in creating new techs such as Deno or Datex (by u/jonasstrehle, unyt.org).
Now if I'm not wrong, C has almost the same level of difficulty, but is faster and yet I don't see a large community of frameworks for web dev, app dev, game dev, blockchain etc.
Why is that? And before any Rustaceans, roast me, I'm new and just trying to reason guys.
To me it just seems, that any capabilities that Rust has as a programming language, C has them and the missing part is community.
Also, C++ has more support then C does, what is this? (And before anyone says anything, yes I'll post this question on subreddit for Rust as well, don't worry, just taking opinions from everywhere)
Lastly, do you think if C gets some cool frameworks it may fly high?
Videos
I know a lot of people go back and fourth about "Why is Rust faster than C" when it's really not, it's basically the same (in general use) but I've seen far less about why Rust isn't faster than C.
I remember a lot of times where people would create (accidentally or intentionally for the purposes of demonstration) microbenchmarks where something like Javascript would actually be able to outperform C because the JIT was able to identify patterns in the execution and over-optimize compared to what the C compiler could do. While this is a great illustration of the flaws with micro-benchmarking since we all generally understand that, no, Javascript is not actually faster than C, (*in basically any real-world usecase) but it's been stuck in my head because Rust should have that sort of information too.
Some information will only ever be known at runtime, such as exact usage/call patterns and whatnot, but if we're speaking in generalities then the Rust compiler should have far more information about how it can optimize than the C compiler ever did, so why isn't that manifesting in an overall speed increase? (again, this is speaking in general, real-world usage, not exact cases) I know there are some cases where this information is leveraged, for instance I remember someone mentioning using a non-zero type would let the compiler know it didn't have to check to prevent a division-by-zero error, but by and large Rust seems more or less directly comparable to C. (maybe low-single digit % slower)
Do the extra safety checks just tend to cancel-out with the performance-gains from extra optimization information? Is it a limitation with using LLVM compilation? (for instance, I've heard people mention that GCC-compiled-C is actually marginally faster than Clang-compiled-C) Or is it just that it's already fast enough and it's not worth the effort to add these performance boosts since their yield is lower than the effort it'd take to develop them? (not to mention if they present issues for long-term maintenance)
To be clear, this isn't a critique, it's a curiosity. Rust is already basically as fast as C and C is basically the diamond-standard in terms of performance. I'm not saying that it's a problem that Rust isn't faster than C, I'm just asking why that is the case. My question is purely about why the explicivity of Rust isn't able to be leveraged for generally faster performance on a broad-stroke technical level. E.g. : "Why is javascript slower than C" -> "It's an extremely high level interpreted language whereas C compiles to straight machine code", "well actu-" shut. This is an actualless question. Sometimes Javascript is faster than C and if you put a pig in a plane it can fall with style, technical "well actually"s just muddy the conversation. So, speaking in broad-strokes and out of purely technical curiosity, why isn't Rust faster than C?
Hello all, I’m putting this question in this thread purposely because I really want to hear from experienced Rust programmers (whether they be professional or unpaid proficient users of Rust) when they think C is actually a better language choice for a given project.
Based on your experience with Rust, what project types would you still elect to write in C?
Talking purely about performance, not development or compile time.
Also, I understand they would be lower level projects, but I am more curious about actual specific things you would build in C over Rust.
I have been researching about Rust and it just made me curious, Rust has:
Pretty hard syntax.
Low level langauge.
Slowest compile time.
And yet, Rust has:
A huge community.
A lot of frameworks.
Widely being used in creating new techs such as Deno or Datex (by u/jonasstrehle, unyt.org).
Now if I'm not wrong, C has almost the same level of difficulty, but is faster and yet I don't see a large community of frameworks for web dev, app dev, game dev, blockchain etc.
Why is that? And before any Rustaceans, roast me, I'm new and just trying to reason guys.
To me it just seems, that any capabilities that Rust has as a programming language, C has them and the missing part is community.
Also, C++ has more support then C does, what is this? (And before anyone says anything, yes I'll post this question on subreddit for Rust as well, don't worry, just taking opinions from everywhere)
Lastly, do you think if C gets some cool frameworks it may fly high?
You seem to have quite a bit of misconceptions, which I'll address alongside your question. If anything is still unclear, please feel free to comment on this answer so I can address it
Safer
Yes, Rust is safer.
Safety is one of Rust raison d'être, after all. Specifically, outside of unsafe blocks, Rust is memory safe and type safe, that is:
- Memory safe: it is not possible to access memory that has not been allocated, or has already been deallocated.
- Type safe: it is not possible to interpret a memory as if it held the value of a given type, while it actually holds the value of another.
Furthermore, a number of operations that would be Undefined Behavior in C or C++ have well defined semantics in Rust instead, such as signed integer overflows.
Memory Safety -- which underpins Type Safety -- is generally further split into two categories:
- Spatial Safety: the inability to access out of bounds.
- Temporal Safety: the inability to access before allocation/after deallocation.
Rust achieves Temporal Safety by compile-time checks. This does mean a bit more code in the compiler1, but there's no run-time footprint -- whether memory or instructions -- so it's "free" at run-time.
Rust achieves Spatial Safety with a combination of library-provided abstractions, and run-time checks. For example, iteration in Rust is achieved by for x in collection. For an array this boils down to pointer increment -- just like in C or C++, so no run-time overhead -- except that since the user doesn't manipulate the pointers directly, it's safe.
Apart from that, there are various run-time checks: bounds-checks for index access2, null-checks for Option, etc... those may or may not be optimized, and thus may lead to a slight run-time overhead. The "trick" of Rust, however, is that if performance really matters (as profiled) and the optimizer is not managing to optimize well-enough, it's always possible to try and massage the code (front-loading a bounds-check, for example) or in the worst case to delve down to unsafe Rust, and thus achieve the required performance target with only localized "Here Be Dragons" code.
1 Rust compile times are on-par with C++, hence quite slower than C, but the safety checks (borrow checks) are an insignificant part of that. Extensive use of generics, traits, and type-inference make the language much more difficult to compile, and the compilation model (full crate at a time) makes parallelization trickier. Still, work is in progress to parallelize the rustc front-end, which should bring compile times down substantially from 2024 onwards.
2 Rust does NOT check indexes at compile-time in general but checks them at run-time instead. A branch itself is typically not a problem at CPU level, if well-predicted. The real impact of bounds-checks is that unless optimized out, they prevent auto-vectorization and a number of other optimizations. This is why front-loading a bounds-check (before the loop) can be so effective performance-wise: it may unlock all those optimizations.
Faster
Idiomatic Rust is likely faster.
First of all, in a "pedal to the metal" situation, all 3 languages can achieve the same performance. Heck, all 3 languages allow embedding assembly, if it comes to that.
So, what we are really asking, is whether idiomatic (not pessimized, not maximally optimized) code may be faster in one language or another, and in those conditions Rust has a certain number of advantages.
The first and foremost advantage of Rust is one of culture. The Rust community is very concerned about performance. This may seem trivial, but it has deep implications. Most notably, Rust code is always compiled from source, and there is no stable ABI (yet). This means that even the standard library provided data-structures can be remodeled extensively as long as their API is left untouched, and leads to Rust having the best-performing HashMap3 of all languages in its standard library:
- It started with Robin-Hood Hashing with Backward Shifting Deletion, which was already faster than
std::unordered_map. - Then moved to a completely different hash-map, based on Swiss Table once Abseil was released.
By comparison, the C++ standard library implementations of std::unordered_map cannot be changed4, nor can their std::regex implementations...
The second advantage of Rust is trust5: the Rust developer can put their trust in the compiler, and count on it to prevent users from accidentally fiddling with private data or violating soundness:
- In C, it's common to forward-declare structs in header, but never expose their definition so users can't (accidentally) fiddle with them. Unfortunately, the use of such "opaque pointers" then is generally followed by heap-allocating the struct... which is detrimental to performance. In C++ and Rust, that's never a problem, so C++ and Rust programs allocate less.
- In C and C++, it's common to program defensively. Copies are made when the lifetime of the input is unclear, for example, to avoid use-after-free. In Rust, that's never a problem, so Rust developers are more likely to be brash and avoid copies... knowing the compiler has their back.
And finally, the Rust language has a few tricks up its sleeve that may lead to better code out of the box:
- Rust has fine aliasing control from the get go:
- This means no Strict Aliasing rule is necessary, greatly avoiding
char*(and co) performance pitfalls. - This means
noalias(the LLVM equivalent of__restrict) being used profusely and automatically, as&mut Tis__restrict T*.
- This means no Strict Aliasing rule is necessary, greatly avoiding
- Rust has niche optimizations. That is, it knows some types do not use all their values, and will "pack" enum discriminants in the unused values whenever possible. As a result
Option<bool>is 1 byte (likebool) andOption<NonNull<T>>(an option non-null pointer toT) is the same size as*mut T(a possibly null pointer toT).
(It's not all roses, though, the defined behavior of signed integer overflows prevent a number of integer-based loop optimizations... though the impact of that is likely low)
3 Bryan Cantrill, who used to be a C kernel hacker at Sun Microsystems, was surprised the first time he naively translated a little C program he had around to Rust to get a feel of the language. Being its first Rust foray, he expected his lack of proficiency in the language would result in slow code... but his Rust program ran faster than his C one, on top of being shorter! He double-checked the output, and after concluding both were correct, did the only thing that made sense: he profiled them. It turns out that in his C program he had hand-rolled a quick hash-map implementation, since dependencies are so annoying in C, while in Rust he had just used the standard one... and his quick C implementation was quite naive. Nowadays, Bryan Cantrill is a Rust kernel hacker ;)
4 And that's on top of inheriting a crippling "memory stability" guarantee from std::map, in order to be more of a drop-in replacement.
5 You can't spell Trust without Rust.
The assumptions are wrong twice:
- safer code does not necessarily mean more instructions, when the safety is obtained with safer language design and rules;
- one language is not faster than another: it's language implementations that are.
About the claim on faster and safer, there is an extensive study comparing performances of languages that concludes with a benchmark that Rust can be faster and more energy efficient than C++. At the same time, you can't have it all and it appears that for this specific benchmark, C++ is more memory efficient.
But general statements about language performance and safety are always misleading:
- Benchmarks are only valid for one kind of problem and with a given algorithm. If you look at the details of the study, you'll find out that for 2 among the 10 benchmarks (e.g. binary trees), C++ is faster and more energy efficient.
- Benchmarks measure a particular implementation and not a general language. For C++ there can be much slower code depending on compiler options. And you can have compilers with higher or lower performance for the same language.
- Real life software also often require use of libraries or frameworks, and safety is then the one of the weakest link in the chain (Update: 2024 article What Recent Vulnerabilities Mean to Rust from the SEI, shows two interesting non-Rust examples that affected Rust).
My advice: choose the language that is the most suitable for the kind of problems you are trying to solve.
Disclosure: I'm not a Rustler