Unless he means that Rust should be used on projects where the Rust compiler is available for all relevant platforms and C and C++ can be used otherwise, I disagree.
In fact, until LLVM is replaced, C++ will probably be the language of choice for new languages. Even `rustc` requires a C++ bootstrap for that purpose.
There are also other important C++ and C libraries that will continue to mean C and C++ may be better choices than Rust in certain cases.
And don't get me started on embedded.
Also, that doesn't even matter when the purpose is a hobby project (such as mine) where the programmer has no interest in Rust (such as me).
I like how Trevor Jim put it [1] better:
> If you decide to write your project in C/C++ you must have a plan for handling the inevitable memory corruption in advance. Anything else is malpractice.
This is possible to do. I've released a non-trivial program that has had no known memory bugs ever since 1.0 on 2018-10-26. It is written in C. And I challenge anyone to find one in any release since then. You might find one, but I think it will be tough.
In other words, it's possible to put C and C++ on the same footing as Rust when it comes to memory bugs; after all, even Rust is not perfect there.
Yes, it takes more effort. A lot more effort. For my hobby projects, that works fine because I'd rather work in C with the extra effort than in Rust.
Oh, and it's possible to make C as good as Rust: I've got macros to do automatic bounds checking. I've got RAII and stack traces. I've got structured concurrency, which will give the same capabilities as the Rust borrow checker if you adjust your coding style. And all this in portable C11.
So no, Rust is not the end-all be-all of languages. Sorry.
Rant about absolutist opinion over.
But seriously, use Rust if it's available for your target platforms, and you have no other preference.
If we are offering absolutist opinions, I would like to offer a counterpoint.
>I've got macros to do automatic bounds checking. I've got RAII and stack traces. I've got structured concurrency, which will give the same capabilities as the Rust borrow checker if you adjust your coding style. And all this in portable C11.
You can do this all in C but it is an absolute nightmare. Every large C program grows some strange bespoke and incompatible versions of all that stuff at some point. And they're all a pain to use because none of it is idiomatic to the language. It's seriously awful. Things like the Linux kernel are the worst offenders. I will be happy when the C language finally reaches the end of its miserable, slow, agonizing death.
The argument you are missing: Your position is bespoke and incompatible. You don't use idioms in your C code. You use idiolect. Thus, you exist in your own universe and are a party of one.
And the clue was something you might not expect...
I programmed C++ for five years, and C for over ten years before that. I don't know rust, but I followed this discussion with great interest.
One of the topics that came up several times was being able to identify who wrote what C++ code based upon their choice of language subset without using blame.
In the pro-rust arguments and C++ counter-arguments and rust counter-counter-arguments and C++ counter-counter-counter arguments in discussions above yours, I was pleasantly surprised to see that grandfather comments were rarely (never?) the same author. At least the rust and C++ arguments were made by different people sharing some sort of group mentality.
Then I hit your thread and saw the grandchild comment: "I understand you think it's awful, but I do like it. Everyone has their preference." and I was like: "Okay. I bet grandparent is also ghoward."
The simple fact that no one is chiming in to grandchild your arguments is the point that you're missing. In fact, the entire subthreads you've spawned involve basically ghoward defending ghoward's position.
It's nice when other people can take over your work. That's what we're after.
I prefer to finish software. [1] I get it to where it needs to be and put it in maintenance mode.
I don't accept outside contributions. [2]
I prefer to work alone to keep the scope of my software manageable and to reduce communication overhead. And to avoid working with people. People are too complicated.
I obsessively document my software. [3]
I comment all of my code. I wrote design documents and requirements lists. I write documents about the source code, its concepts, and how to understand and read it. I turn my code into something that can be studied and used far into the future.
Most programmers are not like Donald Knuth. But there are a few that are. I'm one of them.
Please let me be like that. Don't make me work like everyone else because I can't; I've tried.
I'm fine if you all want to use Rust. I even said to use it by default in my first post.
You all seem unhappy that I do not want to use Rust. I don't get why.
I'm defending my position because it appears you all think it's not acceptable. You're wrong.
I was going to start this reply by saying "I am not necessarily defending / supporting ghoward's position", but after I read his whole reply, I realize I am.
I enjoy writing C (more than C++); I do not find it awful. If you cannot accept that, then there is nothing I can say to change your mind. What I don't understand is this atavistic obsession that "everyone must migrate to rust now", and "C is so dangerous in can explode in your hands while you are sleeping".
Please, go forth and multiply, and use rust to your heart's content. But be more open minded to the fact that there are people who like, enjoy or even love using C. As for me, I admit I have gone from interested in rust, to neutral, to an active dislike, because this narrow-mindedness some of its proponents show.
You do you. Maybe it isn't said often enough, but each side (Rust-lovers and Rust-haters) has to be confident enough to allow others to disagree.
However, let's be serious, the anti-Rust crowd has not been some bastion of high-minded virtue with its flimsy arguments ("Just write better code..." and "Modern C++ doesn't have these issues..."), mole hill matters of taste ("Egads! The syntax!"), drive by hype hate, and unexplained red-herring cul de sacs ("I doesn't have a spec!" Okay, why do you need a spec?).
> I'm defending my position because it appears you all think it's not acceptable. You're wrong.
I'm not sure this and sentiments like it represent something less narrow-minded? Most of the time, it seems kinda resentful?
Because it is. And it is because you all say things that imply people like me are terrible, awful, evil, no-good, very bad people for not using Rust.
We are not. We just have different preferences.
You all have also implied that we are negligent for using C. I don't know about others, but I have not been.
That's why I have the challenge to break a release of my `bc`. I actually have not been negligent because I do put in the effort required to eliminate memory bugs as much as possible.
Until the RESF refrains from implying we are bad or negligent for not using Rust, we will be resentful.
> You all have also implied that we are negligent for using C. I don't know about others, but I have not been.
I’m sorry that people have implied that, but I feel most of the time when Rust people say languages like C should be retired or deprecated, are doing so because your skill and attention to detail does not scale.
Maybe you personally have advanced enough to write safe code, and you have the requisite skills and insight to avoid any and all memory bugs, such that your C code is just as safe as what Rust would compile. That’s great.
But there’s always someone starting out and they will not have your skills. They will make the use after free, null pointer deference, buffer overflows, and other issues that the C language not only allow but encourage.
So the question is: how long do we tolerate new programmers making these mistakes in production code, leading to bugs and exploits for all, when other languages are readily available that would have caught those mistakes before reaching production? C was released 50 years ago, and in the intervening time a lot has been learned about how to write programs, and what language features are desirable in enforcing those practices. We also learned that when practiced aren’t enforced, they aren’t followed. So “just write modern c++”, which is espoused throughout this thread, doesn’t work to make c++ code safer, because it’s not enforced. Not taking advantage of those lessons learned seems like folly, irrespective of how bug-free your particular C codebase is.
> I’m sorry that people have implied that, but I feel most of the time when Rust people say languages like C should be retired or deprecated, are doing so because your skill and attention to detail does not scale.
This is a fair argument.
On the other hand, I wish we had less software so that attention to detail did not need to scale.
For example, I run a Gentoo machine with the Awesome Window Manager and no systemd. The end result is less than 50 processes running after I login.
My machine is powerful, but if I were running Gnome, it would still feel laggy. With this minimal setup, my machine is snappy even when running an update and compiling Rust, Chrome, Firefox, or anything else. (I did have to make my update script renice Portage to the lowest priority to make that happen, but it works.)
Nevertheless, it is good when less attention to detail is needed.
I'm making my own C replacement, and I'm going to rewrite my current project in it when it's done. It will be about as safe as Rust, if not safer because it will not have async/await.
> Maybe you personally have advanced enough to write safe code, and you have the requisite skills and insight to avoid any and all memory bugs, such that your C code is just as safe as what Rust would compile. That’s great.
Thank you.
> But there’s always someone starting out and they will not have your skills. They will make the use after free, null pointer deference, buffer overflows, and other issues that the C language not only allow but encourage.
Yes, they will. But I view this argument as equivalent to "roll your own crypto."
If no one actually rolls their own crypto, then we as an industry will lose the ability to do so, even when we need to. If nobody uses C, then we as an industry will lose the ability to do so, even when we need to.
When I started writing `bc`, I sucked at C. `bc` was so full of security holes.
So what I did was spend enormous amounts of time on the test suite and asserts and then hooked those up with sanitizers and Valgrind. Patterns of bugs repeated over and over.
But I eventually got to a point where I started to get good. I didn't release a 1.0 before that point; I waited until I was good and had done thorough checks with as many tools as I could learn.
I'm currently learning and implementing crypto. I am following the same process, except that I'll add in professional code audits by actual cryptographers, which I have to pass before I release the code.
I'm doing this because someone has to.
> So the question is: how long do we tolerate new programmers making these mistakes in production code, leading to bugs and exploits for all, when other languages are readily available that would have caught those mistakes before reaching production?
We should not tolerate this in production code, but this is a failure of the industry. Why are beginners writing production code in the first place?
This industry grew too fast for its own good. It would have been better if it had had a master and apprentice model. The best programmers would be "masters" (in the sense of master and apprentice), and juniors would be apprentices who would write code under the master until the master thought them good enough to be journeymen (moving up from junior), and over time, such journeymen would themselves come to be recognized as masters, and the cycle would repeat.
If the industry does not do that, we may soon lose the ability to do things we need to do.
(Funnily enough, this is another argument for less software because there will be less people to write production software, and they'll all be good at it.)
> C was released 50 years ago, and in the intervening time a lot has been learned about how to write programs, and what language features are desirable in enforcing those practices. We also learned that when practiced aren’t enforced, they aren’t followed. So “just write modern c++”, which is espoused throughout this thread, doesn’t work to make c++ code safer, because it’s not enforced. Not taking advantage of those lessons learned seems like folly, irrespective of how bug-free your particular C codebase is.
Surprisingly, I agree with you that it's folly. I really do. In my original post, I ended it by saying
> But seriously, use Rust if it's available for your target platforms, and you have no other preference.
People really should do that, even though I don't like Rust.
All I was doing was just to remind everyone that there is a time and a place for C.
By the way, the reason I don't use Rust is because I'm pretty sure I would actually write buggier code in Rust than in C. The reason for this is async/await: I just can't get it. I mean, I do get it, but the rules seem too complicated for my brain to do as safely as I can do threads and locks.
I think I am one of those people for whom async/await is dangerous, just like there are those that just can't get Lisp or Haskell.
I say this so people know I'm not some great programmer; I have my weaknesses like everybody else.
So it would be folly for me personally to write Rust.
At the same time, yes, it is folly to write anything but Rust if it works for you.
> This industry grew too fast for its own good. It would have been better if it had had a master and apprentice model. The best programmers would be "masters" (in the sense of master and apprentice), and juniors would be apprentices who would write code under the master until the master thought them good enough to be journeymen (moving up from junior), and over time, such journeymen would themselves come to be recognized as masters, and the cycle would repeat.
I 1,000% agree with this perspective. I also actually suspect that some form of professional licensing and/or personal liability would benefit the industry greatly, in that it would give more power to engineers to decline to build software irresponsibly due to pressure from management. Obviously this would be wildly complicated and there would be (potentially intolerable) downsides, but I do wonder what things would look like if we could revoke the licenses of people who repeatedly exercise poor judgment, or if responsible engineers knew they had the backing of a professional organization when pushing back against management.
The irony isn't lost on me that elsewhere in this thread I said that nobody is coming after your software engineering license if you keep writing C :) And I do realize that would probably be a disastrous idea in practice. But in my mind it's great.
And while I do agree that the industry grew too fast for its own good, this is unfortunately the reality we live in. The need for software has exceeded both our ability to produce it reliably and our capacity for training qualified engineers. So we have countless junior developers writing production code without good mentors. And worse, I think we also have a terrifying number of "senior" engineers who are actually junior engineers with a lot of experience but trapped in a local maximum. I don't know how we fix this.
> By the way, the reason I don't use Rust is because I'm pretty sure I would actually write buggier code in Rust than in C. The reason for this is async/await: I just can't get it. I mean, I do get it, but the rules seem too complicated for my brain to do as safely as I can do threads and locks.
I "get" async/await but I… hate that it has to exist. I avoid it in all of my Rust projects. Partly because I don't want to use it, but also because I've never needed to solve a problem in a domain where it would have been invaluable. So just so you know, I've been writing Rust since right around 1.0 and I've been essentially able to just pretend it doesn't exist. I've never used it, but also I've never had to think about not using it if that makes sense.
> I 1,000% agree with this perspective. I also actually suspect that some form of professional licensing and/or personal liability would benefit the industry greatly, in that it would give more power to engineers to decline to build software irresponsibly due to pressure from management.
I also 1,000% agree with this. In fact, if there is any activism I do within the industry, it's pushing for this.
> Obviously this would be wildly complicated and there would be (potentially intolerable) downsides, but I do wonder what things would look like if we could revoke the licenses of people who repeatedly exercise poor judgment, or if responsible engineers knew they had the backing of a professional organization when pushing back against management.
I don't think there would be any downside other than expensive software. But studies have shown that poor software is more expensive!
The one thing that would complicate it is Open Source, but I think that hobbyists should be allowed to practice and put code up with no vetting, just like an engineer might design a shed for his backyard and post it on a blog without his/her seal of approval.
However, if some company decides to use that shed/Open Source code in a project, that company's certified engineers/programmers should be required to vet it the same as they would do for their own code.
And as for the gatekeeping of certification, I think uncertified programmers should be allowed to program, but under the supervision of a certified programmer who accepts liability for their work because that chief programmer should be checking their work.
> The irony isn't lost on me that elsewhere in this thread I said that nobody is coming after your software engineering license if you keep writing C :)
Maybe a litte irony, but I would probably be harsher on myself than you would think.
I think there would probably be a certification for writing C, one for writing Rust, etc.
So I would not be allowed to program as a certified engineer for Rust, but I could probably be certified in C, even though the certification and standard of care for C would be much harder and have a higher bar, for obvious reasons.
I don't know what the industry would set the standard of care for C at, but I personally wouldn't set it lower than my personal standards. If it was set at that, then my license should not be taken away, even if I were paid to work on `bc`.
But if it was set higher, say at the level given to libcurl or SQLite, then yes, I should lose my license if I were working for pay on `bc`, unless I stepped up.
In addition, if I worked on `bc` for pay, my failure to prevent memory bugs in `bcl` recently (the one where Valgrind was not hooked into its tests properly) should, at the very least, put me in front of a licensure board to explain myself. My recent failure to prevent the double-free on `SIGINT` when running expressions should also put me in front of a licensure board to explain myself.
If any paying user had been caused trouble for those bugs (no one ran into them as far as I know), I should have to compensate them for the trouble and probably lose my license for negligence. I was extremely upset that I let those slip through.
So no, I shouldn't lose my license for using C, but there should be one for C, and I should have at least come close to losing it for the recent mistakes that I would consider near-negligence.
> And I do realize that would probably be a disastrous idea in practice. But in my mind it's great.
I want to hear why it might be a disastrous idea. I can't see how it would be because it would lead to less but better quality software, and if bad software actually costs more in the long run, it would actually save the industry money! So I fail to see how it would be disastrous.
Please enlighten me on this; I have to know the weaknesses.
> And while I do agree that the industry grew too fast for its own good, this is unfortunately the reality we live in. The need for software has exceeded both our ability to produce it reliably and our capacity for training qualified engineers. So we have countless junior developers writing production code without good mentors.
Yes, this is the reality. This is why I don't dismiss Rust even though I hate it.
> And worse, I think we also have a terrifying number of "senior" engineers who are actually junior engineers with a lot of experience but trapped in a local maximum.
I really don't want you to be right, but I think you are, and that terrifies me because something bad is going to happen. I don't know how we fix this.
However, the end result of that bad thing might actually be certification and standard of care, which wouldn't be a bad thing.
As they say, regulations are written in blood, and I think that's the only way to fix this.
It's just too bad that people will have to die before that happens. I mean, I thought the Boeing 737 MAX would be the wake-up call, but it wasn't. Sigh...
> I "get" async/await but I… hate that it has to exist. I avoid it in all of my Rust projects. Partly because I don't want to use it, but also because I've never needed to solve a problem in a domain where it would have been invaluable. So just so you know, I've been writing Rust since right around 1.0 and I've been essentially able to just pretend it doesn't exist. I've never used it, but also I've never had to think about not using it if that makes sense.
That makes a lot of sense, and I don't blame you.
It's too bad we had a misunderstanding at the beginning of our conversations about this because I think we would agree more than disagree on the direction the industry would go. Oh well; we got to a point where we cleared up the misunderstanding.
> I want to hear why it might be a disastrous idea. I can't see how it would be because it would lead to less but better quality software, and if bad software actually costs more in the long run, it would actually save the industry money! So I fail to see how it would be disastrous.
My answer to this is probably too large for a reply here, but TL;DR is that software engineering is an enormous, complex field. I do know that there are far more ways to do something like this wrong than there are ways to do it right, but I have no idea what would be the right way. Language-based licensing makes sense, but so does licensing for sub-fields like security-related areas (handling PII, cryptography, even different areas of cryptography), operating systems (Windows, macOS, Linux, *BSD, et al), and large frameworks (Hibernate, Rails, React, etc.). But I imagine in practice things would get real murky real quick.
And it's also something where it could easily become too onerous and impossible to develop software.
> I enjoy writing C (more than C++); I do not find it awful. If you cannot accept that, then there is nothing I can say to change your mind. What I don't understand is this atavistic obsession that "everyone must migrate to rust now"…
Nobody is saying this!
In the original tweet, Mark Russinovich said he thinks C and C++ should be deprecated. I believe if you asked him to elaborate on this, he would say he believes we should stop using them when possible. Not "must". "Should". And further, I suspect he'd clarify that he is imploring this of the industry as a whole, and not at the scale of individual developers.
People still write asm, and nobody is coming for their heads. We need people like that! And we will need people who are skilled in C for the far foreseeable future. But the time has come where there exist alternatives to C and C++ which are superior for a very large bulk of their remaining use-cases. He's not scolding individual engineers who choose to continue writing code in a language he enjoys. He's calling for the industry as a whole to recognize this new reality and move forward with the times.
Part of the issue though can be perceived psychological pressure, that can border on being coercive or even come off as intimidation. Kind of like all the "cool kids" are using X, and if you are not, then something must be wrong with you. And while Mark is probably not trying to purposefully create that kind of environment, remember there are powerful corporate interests involved, that probably wouldn't mind some intimidation or unnecessary peer pressure to promote their agenda.
> In other words, it's possible to put C and C++ on the same footing as Rust when it comes to memory bugs;
No. This is only true if you don't care about occasional bugs. e.g. hobby projects. But if you're talking about mission-critical or security-critical software, there is a gap - or a canyon - between C vs C++ vs Rust. And no amount of extra "efforts" by the programmer's part can bridge those gaps completely.
Mission critical is almost meaningless when applied to the entire software industry. It’s whatever each company wrote their core software in. For twitter Ruby was mission critical, for Facebook it was PHP.
Security critical is also rather meaningless, because the idea that a specific language must be used to write secure software is something that the Rust community is claiming but is otherwise unproven and not particularly popular as an opinion. In fact I’ve seen many counters that nearly any GC language offers the same security guarantees as Rust.
Finally, there’s the mighty inconvenient fact that a large amount of safety-critical software (a well-defined term and domain!) is written in C and C++ with a track record of multiple decades while Rust is completely unproven.
> Finally, there’s the mighty inconvenient fact that a large amount of safety-critical software (a well-defined term and domain!) is written in C and C++ with a track record of multiple decades
>Find a memory bug, any memory bug, in my `bc` after 1.0.
I'm surprised by this confidence given one of the commits on the first page is "Fix a double-free when using expressions and sending SIGINT". And going through the commit history quickly reveals "Fix memory bugs in bcl" (a value not being initialized) and "Fix memory leaks in bcl" (missing dealloc), all within the last two months and all which are trivially not a problem in Rust.
Notice that I said to find a memory bug in a release, not just any commit.
Yes, there will be memory bugs during development, but I usually find them before release. There will definitely be commits fixing memory bugs during development.
The bcl library is an exceptional case, where my test suite did not have sanitizers and Valgrind properly hooked up, and that one was because it went from a global (guaranteed to be zeroed) to allocated.
But I also said to find one in the program, not the library.
I issue that challenge again: find a memory bug in the `bc` or `dc` program in a release after 1.0.
Oh, and the double-free with `SIGINT`? Rust isn't going to help you much there. Signals are not part of Rust's model.
>I issue that challenge again: find a memory bug in the `bc` or `dc` program in a release after 1.0.
Tell me what subset of the repo named "bc" you consider to be "the program"
>Oh, and the double-free with `SIGINT`? Rust isn't going to help you much there. Signals are not part of Rust's model.
Crates like tokio::signal allow a signal to be converted to a stream of events, which can be handled along with any other events in the program, instead of interrupting the current fn in the middle and necessitating longjmp shenanigans. Since the existing stack is not interrupted in any special way, dtors fire as they're supposed to.
tokio::signal-type code would not work for `bc` signals.
`bc` is a special program; most programs are I/O-bound, but `bc` is both I/O- and CPU-bound. And it's interactive, so it's got to respond to the user as fast as possible.
It's quite possible for a user to enter an expression, not realizing how expensive it is to compute (since `bc`'s numbers can be arbitrarily large). In that case, a `SIGINT` should be responded to instantly.
Turning signals into an event stream to read would not do that because the signal handler literally has to `longjmp()` out of itself.
It won't `longjmp()` out if it's not safe to do so, but it can be safe to do so. And when it is not safe to do so, it will set a flag and return normally.
Then the code that wasn't safe to jump around finishes, it will check the flag and jump itself once it is safe.
This is what keeps my `bc` responsive even if you have a long-running computation.
To see this, compile and run my `bc`, and give it this input:
>>> 2^2^32
It will hang because it's calculating a LARGE number.
Press Ctrl+C. It will instantly return to the input prompt.
I might be wrong that tokio::signal cannot do that, and if so, I apologize. But it doesn't appear so from your description.
Also, I have the same sort of code in my new project to turn signals into an event stream in C.
Okay, so I didn't say a release at first, but I meant to. That's only fair.
Anything built without the -a option to configure.sh is "the program." That's the vast majority. You can try the library too, but there is that one mistake you already know about. I don't think there are any others though.
And again, even with the things you all have found that are in random commits, it doesn't really matter; Rust isn't completely memory safe either. It's close, but not quite.
"Find a memory bug in this repo. No, not those bugs. No, the ones in that part of the program are off limits. The ones I pushed but caught just before tagging don't count either because I noticed them just in time. Okay there was this one time where it happened when my complicated sanitizer setup broke without me realizing but that was a total fluke. And that other one also doesn't count because it definitely wouldn't have been stopped by Rust[1]."
I'm almost tempted to believe this entire exchange was an elaborate setup to convince people to use memory-safe languages.
To what end? You’ve already stated elsewhere that your only point is that you can, under an increasingly strict set of conditions, write code that is (almost always) free of memory bugs. Great!
Nobody is hiding around the corner waiting to take `gcc` or `clang` away from you if you want to keep using them. Nobody's threatening to take away your software engineering license if we catch you writing a memory bug.
This whole thread is ridiculous. “The safety improvements in modern cars are substantial. If you're buying a new car these days, you should probably buy one made after 2018.” “Well I'm still driving my 1969 Malibu. Plus I’ve never been in an accident so I don’t need those safety features.” “Okay but… what are those dents in your car?” “I said no accidents on public roads. Prove me wrong or STFU.”
I'm sorry, but this is an entirely imagined persecution. Nobody is taking your C compiler away from you. Nobody thinks you're a bad person for using C. Nobody thinks you're a bad programmer for using C.
Many of us think there are better alternatives. We're excited about these better alternatives. We want people to use these better alternatives. We think these better alternative solve a lot of problems that many people spend a lot of time dealing with. We think these better alternatives prevent many of the types of bugs that regularly ruin people's afternoons, evenings, and weekends. We really think you should try it out, but if you don't like it, literally nobody is coming to take your compiler away from you.
We do think a lot of the common arguments against moving to these languages are bullshit though. "Rust can't do self-referential data structures" is one of them. "I have a ton of expertise in C and it would take more time than I'm willing to spend to get to that level in something else", "this project is already written in C and mature", and "I write code for unsupported platforms" aren't bullshit, but they are ones that naturally decay in how compelling they are over time.
And as much as I believe you personally can write memory-safe programs in C, I would be willing to bet this guarantee would go out the window the moment you add a second maintainer who can push and release code without you vetting every commit.
> I'm sorry, but this is an entirely imagined persecution. Nobody is taking your C compiler away from you. Nobody thinks you're a bad person for using C. Nobody thinks you're a bad programmer for using C.
I've had people say all of these things to me in various ways.
"You should be ashamed of yourself," is a typical phrase used.
> Many of us think there are better alternatives. We're excited about these better alternatives. We want people to use these better alternatives. We think these better alternative solve a lot of problems that many people spend a lot of time dealing with. We think these better alternatives prevent many of the types of bugs that regularly ruin people's afternoons, evenings, and weekends.
This excitement has been over-the-top and as mentioned by one or more people in this thread, it has actually driven people away from Rust. As much as I don't like Rust, I don't want that to happen.
> We really think you should try it out, but if you don't like it, literally nobody is coming to take your compiler away from you.
When people say they want to deprecate C, that's what they mean. If you deprecate C, the compilers will be abandoned, which in the software world converges to the same effect as taking it away because compilers need ongoing maintenance to keep up with new platforms and such.
If a new platform becomes mainstream, and there's no C compiler for it, then my C compiler has effectively been taken away.
This is one reason I'm making my C replacement language: I do think that C will eventually be abandoned on the major platforms, and I want to have switched by that time.
> We do think a lot of the common arguments against moving to these languages are bull** though.
Which is fair. You didn't say all of them are, and yes, a lot of the common arguments are crud.
> "Rust can't do self-referential data structures" is one of them.
I actually agree with you; this is a crud argument. In order to make my C as safe as possible, I don't use self-referential data structures either; I use a parent data structure.
I did this with linked lists [1]. The linked list is a parent data structure that owns everything, and the items in the linked list just have indices to their neighbors. This allows me to tie Rust-like lifetimes to the items.
And any self-referential data structure can be expressed in terms of a parent data structure and children data structures.
So yes, you are right: this argument is crud.
> "I have a ton of expertise in C and it would take more time than I'm willing to spend to get to that level in something else", "this project is already written in C and mature", and "I write code for unsupported platforms" aren't bull**, but they are ones that naturally decay in how compelling they are over time.
I agree. This is another reason I'm still writing a C replacement for myself.
> And as much as I believe you personally can write memory-safe programs in C, I would be willing to bet this guarantee would go out the window the moment you add a second maintainer who can push and release code without you vetting every commit.
Yep. I agree. Even if it's Linus Torvalds, Ken Thompson, or anyone.
I still vet every commit, or just not accept anything.
It also helps me keep the scope manageable: if I can't do it myself, the scope is too large.
> "You should be ashamed of yourself," is a typical phrase used.
I'm trying very hard to not "no true scotsman" myself here. But I tend to think of it like this: there's people, and then there's Twitter personas. There's a lot of very strong, absurd opinions that Twitter personas have but that I don't ever seem to encounter in the real world.
Maybe another way of putting it is that if I dropped you on stage in the middle of a RustConf keynote, nobody would actually have these types of opinions. And if anyone did express such a thing, there would be overwhelming opposition to it. But of course I can't prove this, and I'd be willing to admit I'm wrong. And none of this discounts the fact that you've apparently seen these types of opinions firsthand.
> > We really think you should try it out, but if you don't like it, literally nobody is coming to take your compiler away from you.
> When people say they want to deprecate C, that's what they mean.
I don't think that's a reasonable take at all. I'd be willing to bet that what Mark Russinovich was saying was exactly what you just stated in another thread: "I agree with this. I never said people should use C by default; absolutely not. They should use Rust by default."
> If a new platform becomes mainstream, and there's no C compiler for it, then my C compiler has effectively been taken away.
Being taken away is very, very different from other people not being sufficiently motivated to build something you want to exist. Nobody has "taken away" COBOL from folks by not porting compilers to arm64 (I have no idea if arm64 COBOL compilers exist, feel free to substitute a suitable arch/lang).
> This is one reason I'm making my C replacement language: I do think that C will eventually be abandoned on the major platforms, and I want to have switched by that time.
I'd be willing to bet substantial sums of money that "eventually" will not include either of our lifetimes. And my guess is it would be far less work to port C to (or write an LLVM backend for) x86-128 or aarch256 or RISC IX than to maintain your own language and keep it running on all the major platforms. But good luck to you nonetheless.
> Yep. I agree. Even if it's Linus Torvalds, Ken Thompson, or anyone.
I'm pleased you pointed this out! I'd debated saying this but opted not to, it's great that we agree on that extra detail.
> I'm trying very hard to not "no true scotsman" myself here. But I tend to think of it like this: there's people, and then there's Twitter personas. There's a lot of very strong, absurd opinions that Twitter personas have but that I don't ever seem to encounter in the real world.
If we expand that to Reddit and Hacker News, I have to agree, so it might have just been personas I went up against.
> Maybe another way of putting it is that if I dropped you on stage in the middle of a RustConf keynote, nobody would actually have these types of opinions. And if anyone did express such a thing, there would be overwhelming opposition to it. But of course I can't prove this, and I'd be willing to admit I'm wrong.
But I've never been to RustConf, so I can't dispute this. I'll simply have to trust you on this one.
Though dropping me of all people into a RustConf keynote would be a wild prank. Hilarious.
> And none of this discounts the fact that you've apparently seen these types of opinions firsthand.
Unless I've only seen personas, as you suspect, and I can't refute that.
> I don't think that's a reasonable take at all. I'd be willing to bet that what Mark Russinovich was saying was exactly what you just stated in another thread: "I agree with this. I never said people should use C by default; absolutely not. They should use Rust by default."
I'm not so sure because I used to be one of the people that thought C should die, as in, have everything rewritten into another language. People like that do exist.
But again, they may just be personas. Whoops.
> Being taken away is very, very different from other people not being sufficiently motivated to build something you want to exist. Nobody has "taken away" COBOL from folks by not porting compilers to arm64 (I have no idea if arm64 COBOL compilers exist, feel free to substitute a suitable arch/lang).
This is a fair counterargument. I guess I should correct myself by saying that it would feel like my C compiler would be taken away.
> I'd be willing to bet substantial sums of money that "eventually" will not include either of our lifetimes.
I'm not so sure, but no one can predict the future. :) I wouldn't have thought Rust would end up in the Linux kernel when I first heard of it. Now it's all but certain to happen.
> And my guess is it would be far less work to port C to (or write an LLVM backend for) x86-128 or aarch256 or RISC IX than to maintain your own language and keep it running on all the major platforms. But good luck to you nonetheless.
That's true. It's why my language will bootstrap itself to C99! For every platform that is too much work to support directly, I'll support it by having a C99 compiler for that platform.
C99 should not need much maintenance. I hope. Or someone else will care enough to do it. I hope.
> I'm pleased you pointed this out! I'd debated saying this but opted not to, it's great that we agree on that extra detail.
Good! And you're welcome.
Yeah, I'm picky. I've rewritten code given to me by a FreeBSD kernel developer who is twice my age and much better than me.
> If we expand that to Reddit and Hacker News, I have to agree, so it might have just been personas I went up against.
"Twitter personas" is just my pet name for the phenomenon because it seems strongest (or maybe I just first noticed it) there.
> But I've never been to RustConf, so I can't dispute this. I'll simply have to trust you on this one.
Hah! I've never actually been either. I just meant this as a standin for putting you in front of actual, real, physical people who also are likely closer to the evangelist end of the spectrum.
Lest you lump me in with the RESF, I just want to make it clear that all I did was point out your overconfidence in your C abilities. I don't care what language you use or anyone uses. I think Rust has lots of problems that it's stuck with, and I've said on multiple other occasions that the ideal language would be a mix of Rust (borrow checker, typesystem) and Zig (comptime, generics, explicit allocation).
Gavin, there is no point in arguing with fanatics. (You have to know that none of them are actually going to dive into your code and possibly learn something new that didn't come from a vetted authority figure). They are incapable of listening to their own independent thoughts, assuming that they have them, and must attack anyone who counters their dogma because it risks shattering their core beliefs, which is intolerable. The funny thing is that the truth of rust is right there in its name. It is a corrosive process that ruins the iron that it touches.
I really like C. I did actually spend about an hour reading his code. It's very nice, and I genuinely believe that Gavin is probably capable of writing memory-safe C most of the time. Except when his sanitizer setup is accidentally disabled, of course.
I did find at least one case where a function in isolation is explicitly not memory-safe, but its safety depends on implicit and undocumented assumptions about how it's called. I didn't find a way to exploit it from the userland program. In `bc_parse_addNum`[1], a `char *` string is indexed into without checking the length. Callers are expected to provide strings of at least length 2. That expectation is not documented, though it is adhered to. But it is a landmine waiting to be tripped over should that assumption change.
But this is a sideshow. Experts have been insisting for decades that they can write crash-free C and yet we continue to regularly find exploitable memory issues in every large multi-author C project. It's plain as day that—for almost all of us—an increasingly indefensible amount of the time spent on C programs is in dealing with the types of bugs that languages like Rust prevent outright. Whether that's simply finding and fixing them, architecting programs to avoid them, incorporating those fixes into downstream projects, mitigating their impact in live production systems, or whatever, our industry pays an enormous ongoing cost. Even in projects that have never had a public release with a memory bug, significant engineering time is spent on them just during development iteration.
As a handy example, the Linux kernel is still trying to rid itself of `strlcpy`[2] which has been the source of multiple CVEs. This process has taken upwards of twenty years. And that's after spending god knows how much time and effort migrating to `strlcpy` from `strcpy` in the first place.
> I really like C. I did actually spend about an hour reading his code. It's very nice, and I genuinely believe that Gavin is probably capable of writing memory-safe C most of the time.
This is a nice compliment, thank you.
> Except when his sanitizer setup is accidentally disabled, of course.
Fair criticism. XD
> I did find at least one case where a function in isolation is explicitly not memory-safe, but its safety depends on implicit and undocumented assumptions about how it's called. I didn't find a way to exploit it from the userland program. In `bc_parse_addNum`[1], a `char` pointer string is indexed into without checking the length. Callers are expected to provide strings of at least length 2. That expectation is not documented, though it is adhered to. But it is a landmine waiting to be tripped over should that assumption change.
Callers are actually required to pass a string of length 1 including the nul terminator, but you are correct, this assumption is not documented in `bc_parse_addNum()`, and it should be.
By the way, `bc_parse_zero` and bc_parse_one` are constants, so I can make assumptions about them. This is why an empty string is fine: those constants do not have nul terminators at index 0, so if the string parameter does, the short-circuit operators will short-circuit.
I've pushed commit e4b31620f181 to document that assumption, along with a note that assuming a non-empty string might be useful too.
> But this is a sideshow. Experts have been insisting for decades that they can write crash-free C and yet we continue to regularly find exploitable memory issues in every large multi-author C project. It's plain as day that—for almost all of us—an increasingly indefensible amount of the time spent on C programs is in dealing with the types of bugs that languages like Rust prevent outright. Whether that's simply finding and fixing them, architecting programs to avoid them, incorporating those fixes into downstream projects, mitigating their impact in live production systems, or whatever, our industry pays an enormous ongoing cost. Even in projects that have never had a public release with a memory bug, significant engineering time is spent on them just during development iteration.
I agree with this. I never said people should use C by default; absolutely not. They should use Rust by default.
But as I said in another of my comments [1], I think I personally would write buggier code in Rust than in C.
> I've pushed commit e4b31620f181 to document that assumption, along with a note that assuming a non-empty string might be useful too.
Nice!
> By the way, `bc_parse_zero` and bc_parse_one` are constants, so I can make assumptions about them. This is why an empty string is fine: those constants do not have nul terminators at index 0, so if the string parameter does, the short-circuit operators will short-circuit.
Ah, of course. I knew that they were constants, but I didn't consider the short-circuiting of the comparison before indexing further into the string.
> But as I said in another of my comments [1], I think I personally would write buggier code in Rust than in C.
I hope you understand that—all snark aside—I truly, genuinely believe you. I do suspect that if you had invested the just time you've spent figuring out how to work around C's memory-safety shortcomings, your proficiency would be within spitting distance. But obviously those costs are already sunk. And at the end of the day, if you just love C and only want to write C then it doesn't matter what sorts of things another language provides.
I love C, but I've also come to love Rust. Sum types and exhaustive pattern matching are the sorts of things that are hard to live without once you've had them, and now languages that don't offer them are pretty much ruined for me. But at the same time I'm not a fan of the async/await paradigm, though I begrudgingly understand why it was necessary and why it was designed the way it was. For now I simply avoid it, but it makes me sad that we were never able to find something better.
> Ah, of course. I knew that they were constants, but I didn't consider the short-circuiting of the comparison before indexing further into the string.
No worries! That's why I specifically wrote it that way instead of a `strcmp()` call. I may be a little obsessive over getting my code right.
> I hope you understand that—all snark aside—I truly, genuinely believe you.
Thank you.
> I do suspect that if you had invested the just time you've spent figuring out how to work around C's memory-safety shortcomings, your proficiency would be within spitting distance. But obviously those costs are already sunk.
I actually don't know. It's possible, even plausible, that you are correct.
But I've spent gobs of time trying to understand async/await and failed. It wasn't as much time as I spent honing my C, but it was a lot. And I did not seem to make progress.
However, I might have been making progress and didn't realize it. Or maybe an epiphany like the one I experienced with Haskell would have been in the future.
Or maybe my brain just doesn't work in a compatible way with async models. I don't know.
It is one of my regrets that I did not try harder. Oh well; I guess I loved the visible progress of fewer and fewer crashes in a fuzzer more, even if it was slower.
> And at the end of the day, if you just love C and only want to write C then it doesn't matter what sorts of things another language provides.
As long as I am not negligent! I'll add more about this in a reply to another of your comments, though.
> I love C, but I've also come to love Rust. Sum types and exhaustive pattern matching are the sorts of things that are hard to live without once you've had them, and now languages that don't offer them are pretty much ruined for me.
I agree. I love them too, so much so that I run clang with -Weverything to get exhaustive matching in switch statements. And I emulate sum types with enums and unions. I always use switch statements to match on the enum value, which sort of gives me the experience of Rust sum types and exhaustive pattern matching. Sort of.
My language will have them for sure.
> But at the same time I'm not a fan of the async/await paradigm, though I begrudgingly understand why it was necessary and why it was designed the way it was. For now I simply avoid it, but it makes me sad that we were never able to find something better.
I agree. I'm sad about it too. I couldn't really reject Rust if it didn't have async/await, and I wish I didn't have to reject Rust.
(Though the RESF is still off-putting.)
I'm hoping that my language will be a competitor to Rust but allow people to try structured concurrency instead. If Rust still "wins," that's fine; people want async/await. But if they coexist or my language "wins," that's good too.
> But I've spent gobs of time trying to understand async/await and failed. It wasn't as much time as I spent honing my C, but it was a lot. And I did not seem to make progress.
I've said this elsewhere but as a long-time Rust user I have literally never used or even encountered async/await. It exists, and I could use it if I needed to, but I never have nor have I had to spend effort avoiding it or thinking about it. It as far as I can tell a feature you can essentially entirely forget exists.
I'm glad you said this because my impression of Rust async/await until now was that it was unavoidable, mostly because I heard that all of the big-name crates, the ones you would use in just about every project, use async/await.
I think you hear about them more because there’s been a lot of pent up demand for async and people who’ve been waiting for it are excited. But I can count on zero hands the number of times I’ve wanted to use a crate but the only one available required async.
Big-name crates that do IO often use async by default, but I haven't seen one so far that doesn't provide a sync/blocking api as well. Usually this is done by enabling a feature for the crate.
I am also wondering about your confidence given the fact that there are obviously memory bugs during development... what gives you so much confidence that no memory bugs exist in released versions? Do you have an extensive test suite with valgrind /etc to ensure that there aren't any memory bugs before a release but some can slip in in intermediate versions? Or what do you do that gives you the confidence to say that there aren't any memory bugs in the released versions? Or what is involved in all the extra effort it takes to make a memory bug free c program?
Yes, let’s ignore the quite literally millions of examples of prior art for memory bugs in C/C++ programs because you believe you’ve managed to build a single-threaded CLI calculator that doesn’t have any.
I know how to call `strcpy` safely. Are we clear to put that back into the Linux kernel then?
You misunderstood what I mean by "gap" here.
The gap isn't that it is impossible to produce security/reliability equivalent program using C vs Rust, which you seem to be arguing.
The gap is that statistically for many code written in real world for various degree of efforts, Rust code will have much less of security/reliability issues, and it will take much less effort to get to the similar quality point compared to C or C++, and that gap is NOT bridgeable by making C programmer better or by inventing better conventions and styles.
Write a full unit and functional test suite and run them with asan, tsan, msan and maybe valgrind. This has caught all the memory bugs before getting to production in my experience.
You need the test suite to verify that your code does what you think it does anyways, the sanitizers are just a nice bonus to verify that it also has the safety you think it does.
I hate to admit this, but it took me a long time to do, mostly because I was not experienced when I started writing code, and the code has been rewritten several times over to get it right.
First off is the code in [1]. It's technically under several licenses, but I can give it to you under the public domain because bounds checking in C is important enough that I'll give it away.
The `y_ARRAY_TYPE()` macro generates a struct that is an array with bounds. Use it like this:
typedef y_ARRAY_TYPE(uint32_t) uint32_t_arr;
`uint32_t_arr` is a bounded array of `uint32_t`'s.
You can then pass that array to functions and do bounds checks with the `y_i()` macro (for when the struct value is not a pointer) and the `y_ip()` macro (for when the struct value is a pointer).
To implement RAII, the code to do that is in the same repo, but it's...much more complicated: a full stack allocator. I just add the destructor as a parameter when allocating memory, and when the allocation is freed, the destructor is called when it's non-NULL. Then there are functions to allocate and free, but it is always done in a stack, to emulate the real stack.
By the way, the type of destructor is
void (*y_Destructor)(void*)
and it takes a pointer to the item to be destroyed.
Structured concurrency takes the idea of a thread-local stack and sort of generalizes it across threads. The idea is that each thread has a parent, and that parent is not joined until all of its child threads are joined. All threads form a tree, almost exactly like process trees in Linux.
This means that if you want to have another thread borrow an item, you just have to make sure that thread is a descendant of the thread it is borrowing the item from, and you have a guarantee that the item will never go out of scope before the borrowing thread does.
Sometimes you need to change your code to do that. You can "push" things "down the stack" by passing function pointer callbacks to callees who then create the item to borrow and call the callbacks. Inside the callbacks, the borrowed item will always exist.
There's more to it than that (you can turn a thread tree into a DAG [2], allowing you to turn threads that are not descendants of a thread into descendants), but that's probably good enough to start you off.
Dereferencing NULL pointers: my code is structured such that every function parameter that can be NULL is marked so. And I check those possibly-NULL pointers.
This could also be done with a macro:
#define y_d(p) ((p) == NULL ? abort(), *p : *p)
Otherwise, the compiler (clang, usually) warns me when a NULL pointer is passed in and I have asserts to catch them.
Even before I had all of this infrastructure, deferencing NULL pointers was not a problem for me personally; it's just one of the mistakes I tend to not make. So that's why I don't have a macro for it.
For use-after-free, I use scopes. My stack allocator has a function that will mark the start of a scope, and I call it when entering a new scope. Then the pointers that are allocated during that scope will all be freed when I call the function to free every item in the scope, but those pointers will also go out of scope, which means the compiler won't let me access them.
It looks like this:
void
func(size_t s)
{
y_stackallocator* p = y_strucon_stackallocator();
// Random code.
// Open a new scope. Notice that the scope is a bare scope.
// But it could also be an if statement or a loop.
{
size_t* sp = y_stackallocator_malloc(p, sizeof(size_t) * s, NULL);
// Random code.
// This frees everything in the scope, including sp.
y_stackallocator_scopeExit(p);
}
// Scope has exited; sp is not accessible, no use-after-free.
}
This is less rigorous than Rust, but I use the principles in Joel's "Making Wrong Code Look Wrong" [1] here. If I have scopes that don't look like the above, they are wrong.
I get where you're coming from - there are plenty of parts of C++ that I dislike also, but this kind of 'clean up on scope exit' has always been one of C++'s strengths - even before modern c++, and is significantly less error-prone than the solution you present above.
Do you have a tool that enforces the use of all of these macros and stuff? How do you know that, for instance, you didn't forget something? Seems like one slip-up is all it would take.
This is where C fails against Rust. I have to use static analyzers to find such problems.
But I do use them, and I also use sanitizers against large and thorough test suites, with excessive fuzzing thrown in for good measure. And I mix all of that with crash-happy code littered with gobs of `assert()` calls that document as many of my assumptions as I can find.
Those help me find about all I can find.
But I'm still writing a language that is as safe as Rust that I will auto-translate my code into when it's done.
By the way, I'm not using Rust because I don't like it. That doesn't mean it's not good, but I really hate async/await, along with a few other Rust design decisions.
I'm kind of picky as a programmer.
Yes, C fails against Rust here, but that's why I put in the extra effort to bring it up to the same level regardless.
> But I'm still writing a language that is as safe as Rust that I will auto-translate my code into when it's done.
Just curious, how do you intend to make it memory safe? By using a garbage collector, automatic reference counting, a borrow checker, or something else?
By structuring the language around RAII and structured concurrency. More details in my great-great-great-great-grandparent post. [1]
tl;dr: If threads never exit before their descendants, and you only borrow an item in callees and in descendant threads, that would negate the need for a borrow a checker.
As for sharing an item across threads and still not having data races, this will be done by implementing something close to Rust's Send/Sync.
There will be ARC implemented by RAII, just like Rust.
Basically, the language will be built around structured concurrency, including the standard library.
Ah, sorry, my bad, I should have read the whole thread in detail first instead of just skimming it. (:
So do you also intend to completely disallow mutation? Because that's the only way I could see this matching Rust's safety guarantees without actually having a borrow checker.
Say, for example, that you allocate a new string, and you take a reference to it, and then you append to that string within the same scope. This triggers a reallocation which will make that reference invalid. To make that safe you'd either need to have a borrow checker (to be able to detect at compile time that the reference was invalidated), or you'd have to disallow mutation (the act of mutating wouldn't actually mutate that string, but create a new one, leaving the old one alone until it goes out of scope).
Mutation will be disallowed in general, yes. It's a bit more subtle than that, but you get the gist.
In my language, there are strings, which are fixed size and cannot be reallocated, and string builders, which hide the actual string behind another layer of indirection to avoid the problem with mutation.
That sort of pattern would be used as necessary to avoid the problems with mutation in the same thread. And Send/Sync would take care of the rest across threads.
I have macros for that that I use instead of the keywords. If the keywords appear in code that uses scopes like that, it's a bug.
I still have a lot of technical debt with that, but I haven't gotten to it. However, when I do change it, I'll be able to find all instances automatically with grep.
There's a compiler extension to tag variables with a function called on scope exit. Attribute cleanup. For dubious portability reasons I'm unwilling to rely on it for correctness but it's really useful in a debug build for types where you can detect they haven't been cleaned up.
Change MyType to RAIIMyType or similar in the function to get the checking when using GCC/clang with asserts enabled. In practice sometimes types should drop out of scope without deallocation - return from functions etc. Thinking about it now I should probably apply it by default and use (MyType) to avoid the macro expansion selectively.
Let's be honest, `y_i(array, 10)` is much less readable than `array[10]`. Sure, you can program with bounds checking, but it's less readable, and less newbie-friendly.
No macros. This is one spot where Rust is better than C. (Though it is too limiting for my taste.)
Instead, I use ThreadSanitizer and add locks where I may not even need them. Then I may remove some, or change them to different synchronization primitives, and between every change, run TSan several hundred of thousands of times to tease out any race conditions. It's my way of fuzzing for race conditions.
Except that the tweet was pretty absolute. He could have had a reply tweet with something like "unless you have to or you're doing a hobby project." I took issue with the absolutism.
Those qualifications are going to hold for any advice about technology.
You can even justify using malbolge for your hobby project with "I like it" or "It's just for fun".
If your company is only going to pay you for producing malbolge code, then you write malbolge or quit. If you decide to stay, then trying to change their mind might be a good idea if you can spare the effort.
In fact, until LLVM is replaced, C++ will probably be the language of choice for new languages. Even `rustc` requires a C++ bootstrap for that purpose.
There are also other important C++ and C libraries that will continue to mean C and C++ may be better choices than Rust in certain cases.
And don't get me started on embedded.
Also, that doesn't even matter when the purpose is a hobby project (such as mine) where the programmer has no interest in Rust (such as me).
I like how Trevor Jim put it [1] better:
> If you decide to write your project in C/C++ you must have a plan for handling the inevitable memory corruption in advance. Anything else is malpractice.
This is possible to do. I've released a non-trivial program that has had no known memory bugs ever since 1.0 on 2018-10-26. It is written in C. And I challenge anyone to find one in any release since then. You might find one, but I think it will be tough.
In other words, it's possible to put C and C++ on the same footing as Rust when it comes to memory bugs; after all, even Rust is not perfect there.
Yes, it takes more effort. A lot more effort. For my hobby projects, that works fine because I'd rather work in C with the extra effort than in Rust.
Oh, and it's possible to make C as good as Rust: I've got macros to do automatic bounds checking. I've got RAII and stack traces. I've got structured concurrency, which will give the same capabilities as the Rust borrow checker if you adjust your coding style. And all this in portable C11.
So no, Rust is not the end-all be-all of languages. Sorry.
Rant about absolutist opinion over.
But seriously, use Rust if it's available for your target platforms, and you have no other preference.
[1]: http://trevorjim.com/using-an-unsafe-language-is-a-design-fl...