Just a few off the top:
- Rust is a much more complex language than C
- Rust has a much, much slower compiler than pretty much any language out there
- Rust takes most people far longer to "feel" productive
- Rust applications are sometimes (often?) slower than comparable C applications
- Rust applications are sometimes (often?) larger than comparable C applications
You may not value these things, or you may value other things more.
That's completely fine, but please don't pretend as if Rust makes zero trade offs in exchange for the safety that people seem to value so much.
Rust evangelism is probably the worst part of Rust. Shallow comments stating Rust’s superiority read to me like somebody who wants to tell me about Jesus.
If you already dislike this, I ask you to read C-evangelism with respect to the recent Linux drama about Rust in Linux.
but Rust evangelism is on another level
For me Rust isn't really competing against unchecked C. It's competing against Java and boy does the JVM suck outside of server deployments. C gets disqualified from the beginning, so what you're complaining about falls on deaf ears.
I'm personally suffering the consequences of "fast" C code every day. There are days where 30 minutes of my time are being wasted on waiting for antivirus software. Thinks that ought to take 2 seconds take 2 minutes. What's crazy is that in a world filled with C programs, you can't say with a good conscience that antivirus software is unnecessary.
Also, integrating 3rd party code has always been one of the worst parts of writing a C or C++ program. This 3p library uses Autoconf/Automake, that one uses CMake, the other one just ships with a Visual Studio .sln file... I want to integrate them all into my own code base with one build system. That is going to be a few hours or days of sitting there and figuring out which .c and .h files need to be considered, where they are, what build flags and -Ddefines are needed, how the build configuration translates into the right build flags and so on.
On more modern languages, that whole drama is done with pip install or cargo install.
.PHONY: all
all:
cc -o progname -std=c99 -pedantic -Wall -Wextra -Wpedantic -O0 *.c
Or something along those lines. Move some stuff to variables (CC, CFLAGS, etc.) for release. Can use object files for larger programs (but often isn't needed, certainly not when starting out).I do agree that the general experience of Rust is a lot better. But I also think a lot of C projects have overcomplicated build systems that aren't really needed.
Does it? Why? Is it significantly worse than any other language that needs a runtime like Python or Node?
`java -jar foo.jar MainClass` doesn't seem all that bad. Plus you can wrap it in a trivial shell script if you want.
There's nothing special or magic about C code, and, if anything, C has moved further and further away from its "portable assembler" moniker over time. And compilers can emit very similar machine instructions for the same type of algorithm regardless of whether you're writing C, Rust, Go, Zig, etc.
Consider, for example, that clang/LLVM doesn't even really compile C. The C is first translated into LLVM's IR, which is then used to emit machine instructions.
But if you're using it in the sense of "C is a privileged language in terms of its connection to hardware architecture, " well, C isn't, and that statement is patently false. There's not a major difference between C, C++, Rust, Zig--even going as far afield as bytecode languages like Java and C#, or fully interpreted stuff like Python or Perl, especially as far as computer architects are concerned.
(And in the sense of "this is the language that architects care most about for tuning performance," I think that's actually C++, simply because that tends to be the language for the proprietary HPC software that pays the big bucks for compiler support.)
But I don't think this carries much weight anymore, might have been true way back in the days.
C gives you more control, which means it's possible to go faster if you know exactly what you're doing.
I've never seen a good way to make a CPU that's good for "not C" languages. Those are usually by people who are aggressively uninterested in being fast and so insist on semantics that simply wouldn't get faster if done in hardware. Like the way most Haskell programs execute is just bad and based on bad ideas.
But in both cases, modern CPUs are mostly network- then I/O- then memory-bound. Most C programs aren't written to respect that very well.
Feature wise, yes. C forces you to keep a lot of irreducible complexity in your head.
> Rust has a much, much slower compiler than pretty much any language out there
True. But it doesn't matter much in my opinion. A decent PC should be able to grind any Rust project in few seconds.
> Rust applications are sometimes
Sometimes is a weasel word. C is sometimes slower than Java.
> Rust takes most people far longer to "feel" productive
C takes me more time to feel productive. I have to write code, then unit test, then property tests, then run valgrind, check ubsan is on. Make more tests. Do property testing, then fuzz testing.
Or I can write same stuff in Rust and run tests. Run miri and bigger test suite if I'm using unsafe. Maybe fuzz test.
That is demonstrably false, unless your definition of "decent PC" is something that costs $4000.
I love Rust, but saying misleading (at best) things about build times is not a way to evangelize.
How is it demonstrably false? I'm on 5900x and Rust compilation speed was never an issue for me.
More like $1500. $500 for 9950x. $200 for Mobo, $200 for memory and $100 for 1Tb ssd and power supply for $100. Coolers and case by desire. GPU optional.
Only way to get to $4000 in a PC is you are buying fancy components or you bought latest xx90 card.
Faster would obviously be better, but it's not big enough of a deal to cancel out all the advantages compared to C.
So … make && make check ?
https://stackoverflow.com/questions/32127524/how-to-install-...
> I am disappointed with how poorly Rust's build scales, even with the incremental test-utf-8 benchmark which shouldn't be affected that much by adding unrelated files. (...)
> I decided to not port the rest of quick-lint-js to Rust. But... if build times improve significantly, I will change my mind!
Look you're picking a memory unsafe language versus a safe one. Whatever meager gains you save on compilation times (and the link shows the difference is meager if you aren't on a MacOS, which I'm not) will be obliterated by losses in figuring out which UB nasal demon was accidentally released.
This is like that argument that dynamic types save time, because you can catch error in tests. But then have to write more tests to compensate, so you lose time overall.
Have you tried looking around and noticing nobody else does that and it's, like, fine?
Could you cite some examples? There are plenty of counter-examples
- ripgrep is 5-10x faster than grep (https://github.com/BurntSushi/ripgrep/blob/962d47e6a1208cf21...). There's a reason ripgrep is embedded within VS Code to power search.
- Memory-safe implementations of PNG (png, zune-png, wuffs) now dramatically outperform memory-unsafe ones (libpng, spng, stb_image) when decoding images. (https://www.reddit.com/r/rust/comments/1ha7uyi/memorysafe_pn...)
- The Rust implementation of GNU Coreutils compares favourably in performance. For example, uutils/sort outperforms coreutils/sort by 6x while working on every mainstream OS - Linux, FreeBSD, NetBSD, OpenBSD, Illumos, Redox, Android, macOS, and Windows.(https://lwn.net/Articles/1007907/)
- Android rewrote their IPC code (Binder) from C to Rust. The Rust version is within +- 1-2% of the C version (https://www.phoronix.com/news/Google-Linux-Binder-In-Rust).
- rustls outperforms OpenSSL and BoringSSL (https://www.memorysafety.org/blog/rustls-performance-outperf...)
- zlib-rs is the fastest implementation of zlib (https://www.phoronix.com/news/Zlib-rs-0.4.2)
- Advent of Code - The one C example I saw executed the first 10 days of 2024 in 36ms (https://www.reddit.com/r/adventofcode/comments/1hbcyhz/comme...), while my idiomatic Rust solutions (https://github.com/nindalf/advent) took 10.1ms. I'm not even that good, there are Rust solutions 10x faster than mine - https://github.com/maneatingape/advent-of-code-rust.
- I don't consider the benchmarks game a worthwhile comparison because they're only writing assembly, but Rust and C are comparable in speed (https://benchmarksgame-team.pages.debian.net/benchmarksgame/...)
So yes, I'm surprised by your claim of C programs "often" outperforming comparable Rust programs, but I'd be interested to know if even the "sometimes" is true. Please share if you've found any.
The other claims are questionable as well, but this one was the most easily disproven.
Do you mean the "Rust" programs are assembly?
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
No I don't mean. I said what I said.
If you click the C program right next to the Rust program for "spectral-norm" you get the equally unreadable - https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
It is possible to write idiomatic Rust and C. It is possible to write assembly in the form of Rust and C. In both cases, they're comparable in performance.
But leaving toy programs like Benchmarks Game and AoC aside, I gave 6 examples of Rust outperforming C. Do you have any examples of C outperforming Rust?
I don't have a pony in that race.
> uutils/sort outperforms coreutils/sort by 6x
"He used the hyperfine command-line benchmarking tool to run a test ten times; sorting a text file containing all of Shakespeare's works to see which implementation was faster. The first time he performed the test, he used a debug build of the Rust version of sort. In that demo, Rust's version was 1.45x faster than the GNU version. Then he ran the test again using a non-debug version, which showed the Rust version performing the test six times faster than GNU's implementation."
Might it be that a new implementation in C would also have been faster?
The Rust standard library has a state of the art sort implementation. There’s nothing faster, in any language - https://github.com/rust-lang/rust/pull/124032.
And sure, it’s possible that someone could write a C program that compares in speed to all the Rust programs I’ve mentioned. C is a Turing complete language after all. I’m only pointing out that it hasn’t happened in practice.
Also check the Android Binder code before (C https://github.com/torvalds/linux/blob/master/drivers/androi...) and after (Rust - https://android.googlesource.com/platform/frameworks/native/...). Same speed but the quality difference, it’s incomparable.
Because anecdote. We can only know when the thing is designed as an experiment.
(All credit to who-so-ever is working to write improved versions.)
I don’t think anyone could have said something more rude or ignorant if they tried.
> designed as an experiment
Ha. As if you could design such an experiment. What, you’ll place two rats at a keyboard and ask them to implement grep? Bffr.
I’m pointing out the obvious - no one has actually written these mythical C programs that outperform Rust, to say nothing about security and reliability. You’ve deluded yourself into thinking that all it needs is “an experiment”. lol.
* Rust is vastly easier to get started with as a new programmer than C or C++. The quality and availability of documentation, tutorials, tooling, ease of installation, ease of dependency management, ease of writing tests, etc. Learning C basically requires learning make / cmake / meson on top of the language, and maybe Valgrind and the various sanitizers too. C's "simplicity" is not always helpful to someone getting started.
* The Rust compiler isn't particularly slow. LLVM is slow. Monomorphization hurts the language, but any other language that made the same tradeoff would see the same problems. The compiler has also gotten much much faster in the last few years and switching linkers or compiler backends makes a huge difference.
* Orgs that have studied tracked this don't find Rust to be less productive. Within a couple of months programmers tend to be just as if not more productive than they were previously with other languages. The ramp-up is probably slower than, say, Go, but it's not Scala / Haskell. And again, the tooling & built in test framework really helps with productivity.
* Rust applications are very rarely slower than comparable C applications
* Rust applications do tend to be larger than comparable C applications, but largely because of static vs. dynamic linking and larger debuginfo.
Neither of our opinions make someone else's opinion false.
- Rust may have felt easier for you or some, but certainly not everyone or even most. It might be worth it, but it's not an easy on ramp for many.
- Excuses for slow compile times don't make compile times faster.
- That's why I said "feel." There are warm fuzzy and cold prickly human things in here. Studies that pretend at measuring something we all know cannot be measured are summarily dismissed.
- More excuses do not make a statement false. Rust compile times are some of the slowest I have seen in >25 years of development.
Again, the trade-offs work for many people and orgs. That's great!
That doesn't make them disappear or become, "false."
It's precisely this tone and attitude (that is so prevalent in the community) that keeps so many of us away.
On the other hand, C definitely goes too far in to the opposite extreme. I am very tired of reinventing wheels in C because integrating third-party dependencies is even more annoying than writing and maintaining my own versions of common routines.
Both aspects are something I think many developers grow to appreciate eventually.
(And yes, I was considering if I should shout in capslock ;) )
I have seen so many fresh starts in Rust that went great during week 1 and 2 and then they collided with the lifetime annotations and then things very quickly got very messy. Let's store a texture pointer created from an OpenGL context based on in-memory data into a HashMap...
impl<'tex,'gl,'data,'key> GlyphCache<'a> {
Yay? And then your hashmap .or_insert_with fails due to lifetime checks so you need a match on the hashmap entry and now you're doing the key search twice and performance is significantly worse than in C.
Or you need to add a library. In C that's #include and a -l linker flag. In Rust, you now need to work through this:
https://doc.rust-lang.org/cargo/reference/manifest.html
to get a valid Cargo.toml. And make sure you don't name it cargo.toml, or stuff will randomly break.
This is just bizarre to me, the claim that dependency management is easier in C projects than in Rust. It is incredibly rare that adding a dependency to a C project is just an #include and -l flag away. What decent-sized project doesn't use autotools or cmake or meson or whatever? Adding a dependency to any of those build systems is more work than adding a single, short line to Cargo.toml.
And even if you are just using a hand-crafted makefile (no thank you, for any kind of non-trivial, cross-platform project), how do you know that dependency is present on the system? You're basically just ignoring that problem and forcing your users to deal with it.
- compile times
- compile times
- compile times
Not a problem for small utilities, but once you start pulling dependencies... pain is felt.
You can argue that Rust generics are a trivial example of increased complexity vs the C language and I'd kinda agree: except the language would be cumbersome to use without them but with all the undefined C behavior defined. Complexity can't disappear, it can be moved around.
But who cares?
The fact that C chooses not to nail everything down makes it a simpler and more flexible language, which is why it's sometimes preferred.
It makes the C semantics you are coding against more complex. Lots of unlisted or handwaved things in the spec become problems you need to keep in mind far more often than you would with better definitions.
If it's something I'm actively developing, the compile is incremental, so it doesn't take that long.
What does often take longer than I'd like is linking. I need to look into those tricks where you build all the infrequently-changing bits (like third-party dependent crates) into a shared library, and then linking is very quick. For debug builds, this could speed up my development cycle quite a bit.
And implementation wise, probably there's something to do with LLVM.
You don't need to wait for long compile times in Haskell if you don't want to, there are interpreters and REPLs available as well.
You don't need to wait for long compile times in C++ if you don't want to, most folks use binary libraries, not every project is compiled from scratch, there are incremental compilers and linkers, REPLs like ROOT, managed versions with JIT like C++/CLI, and if using modern tooling like Visual C++ or Live++, hot code reloading.
- Project leadership being at the whims of the moderators
- Language complexity
- Openly embracing 3rd party libraries and ecosystems for pretty much anything
- Having to rely on esoteric design choices to wrestle the compiler into using specific optimizations
- The community embracing absurd design complexity like implementing features via extension traits in code sections separated from both where the feature is going to be used and where either the structs and traits are implemented
- A community of zealots
I think the upsides easily outcompete the downsides, but I'd really wish it'd resolve some of these issues...
The difference is C also lets you ignore the inherent complexity, and that's where bugs and vulnerabilities come from.
In C you can ignore whatever you feel like, and that bothers some people so much that they have to stop everyone else from doing it.
The moment you start building something that's not exposed to the internet and hacking it has no implications, C beats it due to simplicity and speed of development .
I don't disagree that Rust might technically be a better option for a new project, but it's still a fairly fast moving language with an ecosystem that hasn't completely settled down. Many are increasingly turned off by the fast changing developer environments and ecosystems, and C provides you with a language and libraries that has already been around for decades and aren't likely to change much.
There are also so many programming concepts and ideas in Rust, which are all fine and useful in their own right, but they are a distraction if you don't need them. Some might say that you could just not use them, but they sneak up on you in third party libraries, code snippets, examples and suggestions from others.
Personally I find C a more cosy language, which is great for just enjoying programming for a bit.
It's not just about security, it's about reliability too. If my program crashes because of a use-after-free or null pointer dereference, I'm going to be pissed off even if there aren't security implications.
I prefer Rust to C for all sorts of projects, even those that will never sit in front of a network.
Also, no: that's only true for some kinds of programs. Rust, c++, and go all have a much easier ecosystem for things like data structures and more complex libraries that make writing many programs much easier than in C.
The only place I find C still useful over one of the other three is embedded, mostly because of the ecosystem, and rust is catching up there also.
(This is somewhat ironic, because I teach a class in C. It remains a useful language when you want someone to quickly see the relationship between the line of code they wrote and the resulting assembly, but it's also fraught - undefined behavior lurks in many places and adds a lot of pain. I will one day switch the class to rust, but I inherited the C version and it takes a while.)
So many people have implemented those data structures though, and they are available freely and openly, you can choose to your liking, i.e. ohash, or uthash, or khash, etc. and that is only for a hash table.
Those complex libraries are out there, too, for C, obviously.
The reason for why it is not in the standard library is obvious enough: there are many ways to implement those data structures, and there is no one size that fits all.
Obviously, all of these languages are capable of doing anything the others can. Turing complete is turing complete. But compare the experience of writing a multithreaded program that has, as part of it, an embedded HTTP server that provides statistics as it runs. It's painful in C, fairly annoying in C++ unless you match well to some existing framework, pretty straightforward in Rust, and kinda trivial in Go.
One comment talked about not using a (faster) B-Tree instead of a AVL-tree in C, because of the complexity (thus maintenance burden and risk of mistakes) it would add to the code.
They were happy to use a B-Tree in Rust though
Rust's safety features help prevent a large class of bugs. Security issues are only one kind of bug.
> C beats it due to simplicity and speed of development
C being faster to develop than Rust is a ludicrous claim.
Rust is a complex language that is safe.
C is a simple language that is unsafe.
There are always compromises and it always depends on the project. Of course importing a dependency is faster in Rust.
But the best language ever imho is Golang. Its simple and safe with the compromise being the GC.
Why is this important? C is the lingua franca of digital infrastructure. Whether that's due to merit or inertia is left as an exercise for the reader. I sure hope your new project isn't meant to supplant that legacy infrastructure, 'cause if it needs to run on legacy hardware, Rust won't work.
This is an incredibly annoying constraint when you're starting a new project, and Rust won't let you because you can't target the platform you need to target. For example, I spent hours building a Rust async runtime for Zephyr, only to discover it can't run on half the platforms Zephyr supports because Rust doesn't ship support for those platforms.
Is it, though? It feels more like how the French saw the French language as "the" language of the world, by basically discounting as unimportant everywhere that didn't use French.
Ok, no, yeah, I see it now. The Lingua Franca is right
Are what cargo, rustc, etc. are expected to run on. You probably meant target.
> i686-unknown-none
Is admittedly a missing target. `x86_64-unknown-none` specifies stuff like `extern "C"`'s ABI (per https://doc.rust-lang.org/rustc/platform-support/x86_64-unkn... ) which is a lot less universal/appropriate for i686, where AFAIK everyone chooses their own different incompatible ABIs - which might be the reason it's not provided? Usually you want to pick an i686-unknown-* target that aligns more closely with your own needs (e.g. your desired object/library/binary file format, abi, bootloader, ...?)
C:\local>rustup target list | findstr i686
i686-linux-android
i686-pc-windows-gnu
i686-pc-windows-gnullvm
i686-pc-windows-msvc (installed)
i686-unknown-freebsd
i686-unknown-linux-gnu
i686-unknown-linux-musl
i686-unknown-uefi
If, truly, none of them are appropriate for your needs, that's when it's time to use a custom target (per https://doc.rust-lang.org/rustc/targets/custom.html ) and `build-std` (per https://doc.rust-lang.org/cargo/reference/unstable.html#buil... .) Using a toolchain file to pin your nightly rustc version might be appropriate (per https://rust-lang.github.io/rustup/overrides.html#the-toolch... .)The last time I played with custom targets was on https://github.com/MaulingMonkey/rust-opendingux-test/tree/m... , using the old `xargo` instead of `build-std`. Notes.md details modifications made to make things work.
Python isn’t simple, it’s a very complex language. And Mojo aims to be a superset of Python - if it’s simple, that’s only because it’s incomplete.