On 1/19/26 10:02 PM, Steve Litt wrote:
That, my friend, is the entire problem. Complexity carries a cost, and
a cost/benefit analysis must be done. There are some folks who like to
add complexity just because they can, whether it carries a net benefit
or not.
Frequently it is because of the "best practices" and trendy "design
patterns" we want to follow, and all the other kids are doing it that way!
I loved Turbo Pascal even more than C. Just as performent, just as
versatile, and more difficult to make a mistake
Yes, it was nice.
By the way, I think C++ sucks big time. I hate C++ as much as I love C.
I was excited by C++. Backwhen I was on a BIX (!), in group about
C++, Stroustrup was there, and for some questions he didn't know the
answer. Which makes sense. C++ started as a hack.
Rust and/or Ada is needed very badly for safety critical applications.
It would seem that using Rust for safety critical applications is a
great idea, but it is actually a little tricky (as safety critical
always is) for two reasons:
1. Part of why Rust is so fast is things like their HashMap (analogous
to a Python dictionary) or Vec (analogous to a Python list). The default
implementations are crazy good. Start with that, use a Vec instead of a
fixed array even for tiny stuff, because it is easy and the overheard is
minimal. Go ahead and abuse it. Later on you might discover that a
different version (BTreeMap? VecDequeue?) is slightly better suited to
some part of your workload, so swap it in, the interface is compatible,
easy to pick a different implementation later. This approach is very
much in the idiom of the language. But all of those fancy data
structures will do allocations for you, under the hood, when they need
to not when you ask. This is not as bad as it looks because there is no
GC, allocations and frees are fast and decently predictable. A Vec or
HashMap can be created using "with_capacity(n)" and the first n items
added will be extremely fast and need no additional allocations. That
value of "n" can even be a constant expression that is computed at
compile time, for zero runtime cost.
2. Rust is all about analyzing the bejesus out of your source code so as
to avoid errors that in other languages have to be debugged at runtime,
possibly with customer data in its mouth. The language is Turing
complete, so one can certainly write bugs into ones business logic, but
there are so many classes of low-level errors that simply cannot exist
in code that compiles. Rust also enforces checking of error returns, one
can ignore an error, but one has to go out of the way to do so.
The result is very robust programs. Experienced programmers come to
expect that if their code compiles it works. So in Rust they offer
(essentially) no way to "catch" an error (arising from a problem that
actually happened god knows where down the call chain), Rust's approach
is to avoid errors in the first place. Try/catch is a mess, no one doing
Rust one much misses it.
Now put it together, allocations are happening all over the place, it is
possible for an allocation to fail, and there is no way to catch that.
Instead the Rust program will panic and die. In most cases this is no
problem. Memory is big these days. And the rare cases where a program
does die are extreme enough that dying is pretty reasonable, and
restarting is frequently a good response.
Except for safety critical embedded cases.
This doesn't mean Rust cannot be used for these cases, but it is a
different style.
The "standard library" in Rust is "std", it has all those fast data
structures. But other libraries ("crates" as Rust calls them) will very
frequently say they can be used "no-std", meaning they can be used
(probably with restrictions) in a non-allocating mode. That, plus other
measures, can make it possible to eliminate ALL sources of panics. Which
is good and suitable for safety critical code. But it can be kind of
clunky for Rust code to have to use the version of Vec that is in the
heapless crate, for example. (Though the compiler will work out all the
details of using whatever kind of Vec, with no run-time cost.)
Rust is being certified to various safety standards, but like all safety
critical programming, programming in that style is rather different from
regular Rust. Safety critical in Rust does not come for free.
If there's anything worth spending complexity on, it's Rust and Ada.
I've heard Rust is getting a lot more complex with time: I hope they
don't go too far.
I dislike that Rust is such a big language. I think they go for a lot of
clever syntactic sugar that they maybe could have been avoided had they
magically anticipated better. Coming from C it looks pretty baroque. It
is also an ambitious language, and that necessarily makes it complicated.
As for "getting" complex, I think that is recent addition of async code.
Async works really well in Rust, but using it adds a lot of complexity.
(They were very slow about declaring their async support as stable, and
it got better because they were so hesitant; the Rust people suffer long
and hard about such things.)
I'm not experienced in async, my reaction is to avoid it if possible.
Sure, if one is in an IO-bound world it makes sense, but even then async
naturally wants to infect all of the rest of your code. There are ways
to avoid that, probably do so if you can.
And Rust developers today complain that the compiler is too slow. It
can sometimes take minutes for a large program to compile. I also
think it is appropriately complex, it is much more complex than was
original C.
What do they consider a "large" program? 1K lines? 10K lines, 100K
lines, 1000K lines?
Here Rust has a surprise for you!
As I said, the Rust compiler is aggressive about analyzing your sources
to the Nth degree. This is good. But it also means all of your
dependencies come as source. (Rust has been very slow about stabilizing
an ABI, and maybe never can.) So it is really easy to make a tiny
addition to "Hello, World!" and have the compile balloon to many
megabytes of additional sources. It doesn't mean your final binary
balloons, but the compiler's work does.
The std library itself is special cased to be a binary (it refrains from
doing things that might always make a stable ABI impossible), so it
isn't quite as bad as it seems.
Luckily, even a Raspberry Pi is far more powerful than was a Cray 1.
Compiles take seconds (longer on a Pi), really bad compile times are
only in the minutes, not hours. And it is getting better.
I briefly studied both Ada and Rust in 2024 and 2025 respectively, and
did a few Programming 101 programs in each. Neither is as
straightforward as C, Turbo Pascal, Cobol, Python, Lua or Basic.
Yes. But appreciate that Python and Lua are so liberal that most bugs
get deferred until runtime. They are approachable languages, but not for
free.
You have to go around safety barriers to accomplish things,
Mostly no.
The very first things I tried to do in Rust were unfortunately hard to
do in Rust. You might have had the same experience.
For example, Rust is vehemently opposed to uninitialized data. Cool. But
I wanted to read a file efficiently, I knew how much space it would
take, I wanted to pre-allocate that for the read.
Nope. Rust does not like that. It wants to allocate storage as the file
is being read, or it wants to zero any allocation you make. This is
important in avoiding undefined behavior, so pretty reasonable.
The answer is (usually) don't do that. Reading the file they way Rust
wants to read the file usually works VERY well. Thinking in 1990s terms
and scale doesn't fit in this case. (Of course, embedded is another matter.)
Another example is Rust is fixated on knowing the ownership of all
memory at all times. The benefits of this are great, but it also makes
self-referential data structures, that are not read-only, kind of
impossible. So if your first project was to implement a doubly-linked
list, you were in trouble. Again, don't do that. There is probably a
pre-built data structure that does what you need.
Rust does have "unsafe" code blocks (std uses unsafe pretty frequently),
where the compiler will relax some of the consistency rules and expect
you to take that responsibility instead, but in practice unsafe blocks
in Rust application code are very rare. In C/C++ every line is unsafe,
in Rust you can easily search for them, have them reviewed extra
carefully, and maybe find ways of removing them. Again, writing your own
doubly-linked list is not advised. (Maybe you wanted to use
std::collections::binary_heap?)
I'm interested in learning Go because I hear Go development is very
quick, and it has a lot of network stuff built in.
Both true. But I was more interested in Go before I learned any.
Four gripes come to mind:
1. Go's design choices seemed odd, like some old man's approach to
scratching his personal itches. It was not at all as carefully thought
through as is Rust. Certainly, if his itches match yours, it might be a
good fit.
2. I was horrified that the Go mutex is not enforced, it is merely
advisory, only used to control access if the programmer feels like it.
Really? Go is only a bit older than Rust, there is no excuse for that.
Too much C influence.
3. Its performance will always be more like Java's and than C's, because
it has a garbage collector. (Rust does not.)
4. It doesn't solve the problem of multi-threaded programs. (Rust does.)
Multi-threaded programs are easy to write, but impossible to maintain
because too much of the code is in the comments or in the original
programmer's head. And if a program is big enough, maintenance will
overlap with writing. Oops.
A word Rust on performance: Rust is a compiled language, has a good
optimizer, with ultimate control in the programmer's lap (including asm
blocks). Both Rust and C/C++ will approach the maximum theoretical
performance of the CPU. Sometimes one will be better, sometimes the
other, and maybe still a little edge to C/C++. But because the Rust
compiler knows more about the code, that should make it possible for
Rust optimization to eventually be better.
There have been a couple notable stories of a programmer who has been
carefully maintaining and optimizing and tweaking his C/C++ program for
years. He (I think the ones I saw were male) has worked hard to squeeze
every ounce of performance out of it. This is a mature program, it works
well. Rust comes along and gets his attention and as a first project he
decides to rewrite the whole program in Rust! And his naïve first
attempt is…significantly FASTER! Startlingly enough to not quite believe
it. In one case I remember there was a followup blog post. He got to the
bottom of it and it was because by just following his nose and doing the
easy thing he had the benefit of the completely general purpose but
still very fast default data structures that come with Rust. C could
have been as fast had it been worth the work, in Rust it was free.
One of the Rust slogans is "zero cost abstractions", that is, features
that make code easier to write and understand, and feel like a dynamic
language, but are fully resolved by the compiler so the program runs as
fast as it would had feature never been used. (Maybe faster, if the
feature allows the program can have a cleaner design.) So I can choose
to put any data type in a HashMap or Vec and the compiler will workout
the specifics, again no run-time charge.
Rust allows one to be more ambitious in the architecture of a program.
Including fearless multi-threading. This can mean significant speed.
Rust is high-level, but about being low-level?
Rust programs can be written to run on bare metal, with no OS. They can
even pretend to do more than one thing at once with async, still with no
OS. (Is a small async runtime an OS?) Rust programs can compile down to
nearly nothing if they do nearly nothing, but to take advantage of what
Rust can do it isn't very practical to fit in some several-KB ROM. And,
the Rust compiler doesn't emit Z-80, 6502, nor 6800 code anyway. (Though
I think there is a useful Rust->C transpiler, to make it possible to run
a not horribly restricted version of Rust anywhere. But still, probably
not practical for a 4K TRS-80.)
Assuming your low-level code is running on reasonable hardware for 2026,
the exact same code might be compiled to run both in a gigantic virtual
environment in the cloud and in a no-OS embedded computer. Very nice
when the two might talk to each other; it is easier to trust the
communications something something is compatible between the two ends if
they are running the same code in on both ends. With each end maybe
using the kind of Vec that makes sense for it. Rust can do that.
Another Rust point I have to add: Because the compiler does so much
analysis of your code, it also knows so much about your code. And in
recent years this has turned into really good error messages from the
compiler. This is part of making Rust a much more productive language,
for those who get over the hump and get good at it.
In other languages a big refactoring can be dangerous and scary, but as
you work through a Rust refactoring the compiler will tell you all the
places you still need to address. Your IDE will help along the way, too.
(I use emacs as my IDE, and I have it knowing Rust pretty well.) Once
the compiler is happy, your code likely works.
Odd coda: The rise of LLMs and "vibe coding" scares the heck out of me.
Turn ChatGPT loose on a Javascript project and it sounds like you'll
quickly get something that works pretty well, but will never work right.
I don't like ChatGPT, so let's turn Claude loose on a Rust project.
I'm not worried. The compiler will complain about any crazy nonsense.
Claude can follow the suggestions from the compiler and it will come up
with something that can be pretty good. And completely trustworthy at
the level of being self-consistent in all the ways the Rust compiler
guarantees. The way that LLMs are just an extremely fancy form of an
autocomplete BS artist fits in really well with all the discipline Rust
imposes.
Disclaimer: I think the reasonable way to do this is with "Claude Code"
and I haven't yet put the $20/month drain on my credit card that it
costs. But I expect I will.
-kb
_______________________________________________
Discuss mailing list
[email protected]
https://lists.blu.org/mailman/listinfo/discuss