I feel the same. I'm tired of languages, can't even muster the energy anymore to give Rust a serious try - although I'm considering I'll have to do it in the future anyway, given that the hype hasn't ebbed away.
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.
I've worked on porting a program written in C to a memory-safe language.
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.
Memory safety issues are not always an issue. What I mean is that having memory safety is not everything, since a system that is memory safe can still contain bugs, and vice versa a system with memory related issue can function perfectly for years, because they are corner cases that are difficult to get into.
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).
Even ignoring memory safety, C allows a lot of footgunning. I've found another source of grief to be weak typing and operators priority. I was very surprised to find bugs in the Linux kernel examples.
> 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/++.
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.
>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?
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.
> if B implements From<A>, then A also implements Into<B>
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?
> can't even muster the energy anymore to give Rust a serious try - although I'm considering I'll have to do it in the future anyway, given that the hype hasn't ebbed away.
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.
> I feel the same. I'm tired of languages, can't even muster the energy anymore to give Rust a serious try - although I'm considering I'll have to do it in the future anyway, given that the hype hasn't ebbed away.
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.
Most of what it offers was already available on Ada and Modula languages, but apparently we needed some fresh air to gather the attention of newer generations.
I want to work on boring languages that let me focus on domains, not tools, and even so I work with Rust and have done in my last 3 or 4 roles. If you do systems programming in a startup, Rust will be there.
Me too (of course), but the last time I had a segfault that took me longer to fix than at most a couple minutes must have been years ago. The last time I got a segfault at all is probably months ago.
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.
Language fatigue and settling on C seems to be a thing, as I've encountered several people with the same opinion. I wonder what the physiology is behind it.
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.
I tried Rust very briefly, a couple times building some random projects. Each would download and build around 500 (!) dependencies. A few of them failed to build on my machine.
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.
> 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?
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.
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.