Counterpoints:
1. 0-terminated C strings are slow. There's the constant need to scan the string to determine its length. Taking substrings requires making a copy. Yes, you can manually do length delineated strings in C, but there's no support for it in the language, it's error-prone, and doesn't interface with anybody else's C code.
2. One thing I've noticed over decades of using C is that it's very brittle. What that means is, once an algorithm is selected, it is never changed because it's too hard to refactor the code. (For example, changing a reference type to a value type means changing all the -> operators to .) This means C programs tend to get stuck in a local optimum of inefficient data structures and algorithms.
3. No array bounds checking, leading to a lot of time lost debugging the #1 programming bug in shipped C programs.
If you're doing RAII in C, you're more than ready to move to a more powerful language.
There is no perfect language, C just seems the best for me to get job done.
This is a silly argument.
For array bounds checking, if you want it, just a write a structure with buffer length and a getter setter and it is done. People complain this write zillions of getter setters in other languages but just choose to complain in case for C.
It's so easy, yet buffer overflows remain the #1 problem in shipped C code.
> For all small strings that just locally it serves its purpose nicely.
Not really. Whenever I review other peoples' C code, I look at their use of strlen/strncpy/strxxx functions. They're a rich source of bugs, and I'll usually find one in it (usually an off-by-one error). They don't have to be large strings, either, to be slow.
You are saying C string is slow, I am telling you short local strings are not slow, please tell me why it is slow in that case?
I used (Wirth's standard) Pascal in our compiler class in college. String handling was actually pleasant in Fortran-77 on VMS, so I figured pure Pascal had the worst string handling of any language -- until I started programming in C. I finally made peace with C strings, but a couple of decades later, Forth said to me, "Here, hold my beer." I wrote some Forth-word equivalents of some of the C Library string functions to make my life a little easier! :)
In D we call them dynamic arrays. They could be called length-delineated strings.
1: https://en.wikipedia.org/wiki/String_(computer_science)#Stri...
Just record it once
I made a proposal to fix this in C, but it went nowhere:
Another trouble with two variables is there's no obvious connection between them. One can be modified without the other, etc.
> Thankfully, many exist, so this is a non issue
Many string libraries that are incompatible with each other. This is a huge issue. (I myself made many C string libraries. It's not so easy. Try it.)
The language extension I proposed for C is the same one D uses. D has had it for 20+ years, and it has proven very, very satisfactory.
If you want automatic bounds checks, then C is not the language for you.
> This is a huge issue.
Could you give an example where it's a huge problem? I'm probably limited by my experience. All of the codebases I've worked on used a single string library. When passing externally/to other libraries, boring C interfaces were used, then those libraries do what they wish from there. The string libraries I've used were mostly, deep down, just structs, with a length member, char pointer member, and encoding stuffs. Passing to the other library almost always ended up just being those member values being passed as arguments to a function, which were copied to the nearly identical structs of the other string library.
So you use a translation layer. Sorry, I just don't like them, but if you're fine putting these on all your interfaces with other libraries, well, what can I say? :-)
> If you want automatic bounds checks, then C is not the language for you.
I would reframe that as: "if you're ok with buffer overflow malware injection, then C is the language for you!" Nobody has yet figured out how to stop that.
The sad thing is it's so fixable with just a minor, compatible change to C.
Write manual bound checks and good code in general? Granted you won't be able to catch every vulnerability, but at some point other vectors are so much easier to exploit that you won't have to worry about these anymore.
I promise I'll write good code from now on. Scout's honor.
40 years of C buffer overflows argues that doesn't work.
There's very little difference in C between a character string, an array of Bytes, or even a struct of appropriate size. Other than the types and other user friendly (relative to assembly) features that C adds. This is deliberate; yep, it makes working with text harder (and maybe slower) than is necessary, but C doesn't assume that text is something you ever need to represent in your programs by default. So if you need to work with a lot of text, you should find a library for it, or write a library for it.
C isn't designed to be fast (though it often is). It's not designed to be safe (though it also often is). It's designed to be extremely precise, like a hardware description, or a good maths paper.
Some people like that precision. More often, people need that level of precision (see Linus' comments about C over C++ in the Linux kernel).
For someone of his computing calibre, I found his take on C++ surprisingly... immature. He never truly justifies exactly why he feels C++ is bad in his rants[0][1]; furthermore, he resorts to rather poor logic, which boils down to essentially C++ is bad because the programmers using it are bad.
Perhaps when he did attempt to use it, C++ was equally as immature, and the tooling and compilers were sub-optimal, and there was no RAII, nor std::algorithm, nor any of the niceties available with C++11 (it got better still with C++20's concepts, ranges, std::format, modules, etc).
I daresay that C++ is equally as precise (honestly, I'm not quite sure how to parse this in context) and perhaps even fills in the gaps where the the programmer's intention and their code diverge.
[0]: https://lwn.net/Articles/249460/
[1]: https://developers.slashdot.org/story/21/04/17/009241/linus-...
For the record, I attempted Rust. While it looks nice, I am personally more partial to the by-default freedoms that C++ gives, together with judicious use of sanitisers. If not a systems language, then I default to .NET.
This is really bad!
> It's designed to be extremely precise, like a hardware description.
This is absolute nonsense. There's far too much undefined behavior in C for it to be described as precise with a straight face.
More generally, C's lack of memory safety has been directly responsible for innumerable vulnerabilities and trillions of dollars in costs to society. It is unacceptable and irresponsible to start a new project that's meant for public use in C in 2022.
If you're a hardware designer building a microcontroller for a gas heater, you can just target the 'C' model, and you can have a high degree of confidence that anything defined by C will work.
This isn't the case for say Python, since lots of behaviour is defined abstractly in terms of API's and program behaviour (what we want the programmer to worry about), not in terms of memory allocation, and register widths etc. That a hardware designer can design to.
To me, C is almost entirely a non-language. It's a culture where you focus on what you want the computer to do (and optimize that), instead of optimizing what you write (and ending up with something convoluted that will be hard to read in 1 month).
I refuse to do things that many don't even give a second thought - such as, do I really need to implement the Into<T> trait and use x.into() to do a conversion? Isn't it better if I just write the function that I'm using explicitly? That way it will be easier to see what function is used, and the likelyhood that I'll have to change that code later I deem comparatively small.
C has its flaws but I know them quite well by know and have learned to walk around them. And it has some "flaws" that are misunderstood strengths to a degree.
More and more "humble" languages have been started in the recent years, but there is little incentive for me to try and switch. And even of those, almost all add some clever things that I'm nervous might be _too_ clever.
On the very first run of the port, immediately, I've found a bug due to C weak typing. After a few days, I've found a few buffer overruns.
My take is that there are two types of programmers: those who admit they can't avoid making memory safety mistakes, and real programmers, who don't make such mistakes, and happily keep programming in C/++.
EDIT: Just for fun, I've randomly picked up another C program, written in C17/C18. Immediately found a bunch of problems (not sure if there is any impactful, or not), including inconsistent function prototypes.
Of course there are cases where memory safety issue cause security problems, but not in all cases. If for example by a very uncommon sequence of keypress on my washing machine I cause a buffer overflow, worse case scenario the microcontroller hangs and I have to unplug it and plug it back in again (but hopefully there is a watchdog that resets the processor automatically). A lot of C programs don't even have an user interface, because for example are embedded in device that has no external input (for example a microcontroller that manages the operation of a power supply).
I absolutely agree. There's a class of programmers that would rarely if ever make such mistakes (e.g., Torvalds), and to them, C is freeing.
For the rest of us lesser programmers, the handrails of a borrow checker are necessary.
Firstly, you would implement From rather than Into. Into is blanket-derived for From. So if B implements From<A>, then A also implements Into<B>.
And no, you don't have to do this. If the function is never going to be generic, there's no sense in writing it that way. Solve the problem at hand, you can always go back and add abstraction later.
I really like Rust. I hear people say that it's hard, but I don't think it's harder than the problems it solves.
I think I’m misunderstanding, because I don’t see how this is possible given that you could be losing information in the conversion. Is this only for isomorphic types?
I'm in the same boat. I've been in this industry for a long time and generally good at spotting hype. I try to avoid the endless wheel reinvention that goes on.
That being said... I hate to be the Rust evangelism strike force, but give it a try. The hype is not ebbing away because there's substance there.
IMHO it's the first real alternative to C and C++ that brings a beneficial paradigm shift without sacrificing performance or the ability to code close to the metal. You can write systems code that is provably safe in terms of catastrophic memory errors and is orders of magnitude less likely to have threading bugs. (It's still possible to leak memory or have a deadlock, but it's harder to do and easier to diagnose. More importantly neither of these errors are likely to lead to catastrophic security vulnerabilities.)
As with C there is definitely a learning curve. New C programmers get crashes all over the place until they get it. New Rust programmers get beat up by the borrow checker and other type system stuff until they get it. Luckily they've put a massive amount of work into making the compiler's errors comprehensible.
After getting good with Rust I am now more productive in it than C and C++. It's the first attempt at a C replacement I can say that about, and I've tried a few. The only other languages that are more productive than C are higher level languages with fat runtimes not "close to the metal" languages.
Edit: the part of Rust that garners the most complaints is async, and I'm still a bit on the fence about it. It's usable but needs work in the standard library to solve the "async runtime lock-in" and dependency hell problems. Also needs some better libraries in general. Most of the issues with async are in the libraries (or lack thereof) not the language. It was a mistake not to have the async runtime in "std."
But honestly the fact that you can do async this way safely in a bare metal language is impressive, and the only way to do that much better is fibers (a.k.a. coroutines, go-routines) and that generally requires a fat runtime. Go just compiles a fat runtime into all your binaries to get goroutines.
At this point, it probably won't die out. The time to die was in the beginning, when they set to do overly ambitious things that had a high chance of not working. People have been wanting what it offers for quite a long time.
It's a matter of the more global design - factoring out the nitty-gritty things in some central places removes many bugs without you having to think about it. A lot of "usage" code then looks very simple, almost python-like. Variables and function calls. A few ifs and elses, a little bit of arithmetic.
Look at the Linux kernel for example. It is superficially not pretty, but you have to walk around quite a bit for example to see explicit locks and unlocks. It's a matter of factoring out the hard stuff in central places. This is a skill that is totally unrelated to the language you're using.
Yes, bugs happen to everyone, and more so in C than some other languages. Yes, there are security problems that come from lack of memory safety. But simply in terms of productivity, I feel that C is a very good tradeoff for me.
For me, C is the lingua franca. With C I can read kernel source, I can read systemd source, I can read firmware, boot loader, etc. I want to specialize in the language that gets me the most bang for my buck. Rust is a compelling future, but it's still the future and not the present, and the present doesn't seem to be going anywhere fast.
Another time I decided to try and get OpenGL running on Win32 with Rust in an evening. I failed to find a satisfying way (free of boilerplate and magic incantations) to do it. Probably interfacing with the system isn't that easy and/or you're supposed to use specific wrapper crates. Don't remember the details anymore, but it's definitely true that existing infrastructure has a lot of inertia. What Zig is doing in that space is a smart move - it has a C compiler built in, and if I understand correctly it lets you interface with C system headers pretty seamlessly.
You crazy bastard. I can see you're not going to get promoted by power-hungry bosses anytime soon! Seriously, I love C. As a webapps guy I picked up Go for my latest projects and have loved it. Feels like the C compilers of yore, that ran fast and had straightforward semantics, even in the tooling.
conan(or even the light weight tool called clib) could be of help but I have not tried them, as I don't feel the need yet.
I used c++, golang, python, javascript over the years, even tried Rust briefly. Turns out I'm most productive in C, I can get stuff done the fastest way in C and one month later I can still understand the code.
Posix C with all its libraries can do so many wonders, many of the traps and pitfalls are well known, fuzzing test can further secure the code, and, it's just that unbeatable-ly fast.
Adding a little practical OOD into C if your code base is large, even with dtor|ctor|RAIIs(a bit tricky, but manageable). I call this my C+ style.