But when you start composing things together and juggling the lifetimes of various things, the complexity balloons and it becomes a lot harder to understand "spooky action at a distance"-type things. You might argue that with good structure and discipline it's still not difficult, but the assumptions are all implicit and you have to get into the mind of whoever wrote it in the first place to understand it. And I write a lot of C and still find it tricky when I have to go look at some random C codebase!
Absolutely. C Code is not necessarily easy to understand. Not at all.
But the C parts itself are? In C you seldom wonder what C does, you wonder what the C code does.
If you work like OP likes, single person projects, small enough to keep almost completely in your head, I can see how C is especially charming. You never wrangle with C, only with your own code.
You do realise that C and C++ are among the languages (there is also Perl) having anual obfuscated writing contests, right?
Being self evident is certainly not something I associate with C code.
But usually the most controversial features are:
- use of exceptions
- use of RTTI and dynamic_cast
- use of stdlib types which allocate memory under the hood
- ...or even use of any stdlib types
- has boost or similar complex dependencies
- use of 'modern C++' features that explode compile times (like the range stuff)
...etc etc... when you look at C++ libraries written in the game development or embedded hemisphere, those usually use an entirely different C++ subset than the more academic "modern C++" crowd.
* core, whose only dependencies are six symbols (memcpy, memcmp, memset, strlen, rust_begin_panic, and rust_eh_personality)
* alloc, which layers on top of core, and adds the ability to allocate memory
* std, which includes all of the operating system specific things
But most importantly, you can declare that your code doesn't require std and alloc with an annotation, and there's a way to tag these crates in the package manager as well. Additionally, cargo has a "features" feature, so you can say "please give me the no_std version of the library" and get what fits within your needs.
My team at work is working purely in the 'core' subset, and we're able to easily find and evaluate open source projects that will work for us, because of this.
Oh, I should also mention: while Rust doesn't have exceptions, there's also a way to compile your program so that panics abort instead of unwind the stack, similar to -fno-exceptions being passed when compiling C++ code. There is also a setting you can include so that, if, for some reason, you require the unwinding semantics, if someone tries to build a project with abort semantics and your program, it will not allow it and let you know why. I don't think I've ever seen this annotation on a package in the wild, because since they're not used in the regular course of programming in Rust, virtually every open source library does not use them for important semantics and so therefore works with either setting.