Learning Rust after learning C++

It is subjective, true. I was talking of K&R and Stroustrup's C++ Programming Language (pity it is soooo dated but "The Design and Evolution of C++" was IMO the very best language book I have ever read.)

Also at this stage of Rust's evolution it is a pit much to expect that sort of quality in books. It takes time and experience.

But it is taste, our tastes differ!

That is indeed a classic book. In it Stroustrup discusses pretty much every design decision in the creation of the C++ syntax and semantics in a very clear and convincing way. So much so that no matter what aspect of C++ may have made you wonder "why the hell?" when you set out, he totally convinces you that it really had to be that way and that there was no other choice. It is a tour de force.

Thus you buy in to the C++ idea and struggle with it for a couple of decades. All the while getting more and more confounded at how complex writing code can become. And how increasingly complex the language itself is becomes over the years.

All the while there is a nagging doubt in your mind. Something is wrong here you think. There must be a better way. You dare not say such things though, for fear of appearing stupid to your peers. After all, Stroustrup made it all perfectly clear in that book did he not.

The problem is that the whole tower of logic of "the design and evolution" is predicated on a bed of sand. The undefined behavior of C.

Finally the young kids point the finger at the king and say "He's not wearing any clothes!" Finally this all becomes a topic one can discuss in public.

Rust is born.

6 Likes

Brilliant! Another factor in Rust vs C/++ is that the underlying hardware architecture has diverged from that presumed by C/++, from a single-thread single-core CPU tied to a 1-level memory hierarchy with uniform access timing, to a multi-core multi-thread-per-core CPU tied to a multi-level memory hierarchy where cache locality issues dominate timing. The latter is one reason why the linked list examples found in all C/++ texts are no longer appropriate basic coding techniques.

5 Likes

This sort of thing is the critical reason (to my mind) of why one should choose Rust (or other "modern") language over C/C++. The problem space has changed and there is no way to backwards-compatibly fix C/C++ to do the "proper" thing for modern hardware.

6 Likes

I can't ague with that.

Except to say that C is a wonderful thing. Simple and almost perfectly formed. It exactly fulfilled the role it was created for, making Unix portable. It has no more and no less than required to get that job done. A single competent programmer can create a C compiler in a reasonable amount of time.

Should we ever have to bootstrap civilization after the apocalypse C will be there, or something very similar.

7 Likes

I would agree. Both C and C++ were absolute godsends when they were created and developed; however, hardware has changed such that basic underpinnings no longer align. C and C++ can't change enough to properly accommodate modern hardware without breaking backwards compatibility in ways they don't seem to want to do.

At this point, C++ is starting to look and feel like the Winchester Mansion. C, on the other hand, looks like a solidly built house from the 1920's to 1940's - made of great materials, wonderful craftsmanship, utilitarian, but, unfortunately, too dated for many uses AND you might get electrocuted by out-dated wiring, poisoned by lead pipes, suffocated by asbestos, and you'll probably spend too much on heating and cooling.

To me, most other "modern" languages like Java, C#, Javascript, etc., are all safer than that, but, lack the utility. Rust, on the other hand, seems like a home built for tomorrow available today for the low, low price of just using it and participating.

Can't beat that to my "Craftsman" type of mind.

7 Likes

And it was conceived before the massive attack vector that is the Internet. That also has to be taken into account. Before global networking, the worst that could happen with a "security issue" was that a bad actor in your own ranks on premise could do some unauthorized things. Bad, yes, but catastrophic, no. This all changed with the advent of the Internet. Now everybody can attack you. All input is untrusted. And we start realizing that buffer overflows, dangling pointers, double frees, etc. has worse consequences than just a segfault.

This is in my mind the most compelling reason to not use C or C++ any more if at all possible. I can make an exception if you're writing the firmware for a dishwasher which can't connect to anything except its own hardware. But that's as far as it goes.

8 Likes

It has been years so memory may have failed, but: My memory is Stroustroup emphasised that C++'s purpose is (was?) to test out language ideas. A research project, all be it a immediatly useful research project. At some point I am sure he said that there would be another language that took that knowledge lessons and... AreWeThereYet?

1 Like

I disagree. Rust is good, but C++ can still be very useful and can be used in all the modern hardware contexts successfully. The C++ folks are still doing very good work.

Any time the discussion turns to highlight advances in modern hardware, I always immediately think back to this paper: C is Not a Low-level Language. It is rather eye-opening. The main takeaway is that programming languages, indeed even Rust, have some catching up to do if they are expected to take full advantage of the multiple levels of parallel processing happening even on a single thread in a modern CPU.

Then there is also the flip-side; that such parallel processing can be dangerous, à la Spectre, Meltdown, Foreshadow, Fallout, and ZombieLoad.

Without getting too off-topic, you can learn C++ and you can learn Rust, but you are still very, very far away from the hardware.

2 Likes

Your link does not work. Do you mean this one : http://delivery.acm.org/10.1145/3220000/3212479/p10-chisnall.pdf?ip=87.100.178.36&id=3212479&acc=OPEN&key=4D4702B0C3E38B35.4D4702B0C3E38B35.4D4702B0C3E38B35.6D218144511F3437&__acm__=1574482786_d5505b84975f13ac4cbea7628d02a2f3

Shame. I changed the link to the HTML version. ACM is doing weird things with anti-download policies.

These exploits are primariy due to attempts to predictively speed up execution of a single thread – the computer model that C is designed for. Parallel processing (i.e., the existence of concurrent threads accessing the same hardware) assists in their exploitation but often is not necessary.

There are serious attempts to build barrel processors using the open RISC-V architecture where multiple threads commutate in a fixed order, precisely so that all the auxiliary thread-speed-enhancing security-vulnerable lookaside memories that are open to timing attacks can be eliminated. Total system throughput in such designs can be the same, but only if there is enough work to keep all the barrel slots active.

2 Likes

That is an interesting paper. I might have to read it through a few times to absorb it all.

Just now I think that if we accept the paper's argument that C is not a low level language we have to conclude that assembler is not a a low level language either! That may seem absurd but it comes about for many of the same reasons:

  1. An assembler programmer has no idea how long any given instruction will take to run. That can vary significantly depending on where that instruction is in the program, or on whether it's data is in cache or not, and which level, on how it is scheduled with other instructions for parallel execution...

  2. An assembler programmer has no idea if his instructions actually get run or not. Speculative execution and Specter/Meltdown issues apply to everything you write in assembler as much as it does C.

  3. In short, there is a lot of that "irrelevant" detail he refers to that we cannot know or control even in assembler. The abstract machine we are programming to is not knowable to a large extent. Shocking but true.

I'm sure we could think of a lot more.

Dang, now my link to the paper does not work either.
It's here if anyone is interested: https://otaniemi.conveqs.fi/public/c_is_not_a_low_level_language.pdf

I thought the same thing initially. But focusing on one statement from that paper might be able to help us reframe the discussion:

A modern Intel processor has up to 180 instructions in flight at a time

This provides a [generous] upper bound to the parallelism available in a single CPU core. Any enterprising human should be able to exploit this feature by hand-writing assembly that can take full advantage of all these lanes of the pipeline. And if this is true, then a high-level language can be designed that optimizes very well for this new "modern Intel abstract machine". Which ultimately means that assembly language is not bound by the same limitations as the C language.

However, this new "modern Intel abstract machine" is doomed to share the same fate as the C abstract machine, in some 40 years time.

I can make a good case that this is not true:

  1. Over the history of CPU design there have been many attempts offering such fine grained instruction parallelisim to the human assembler programmer for his explicit control.(and to compiler writes of course). They all failed to meet expectations.

The Very Long Instruction Work (VLIW) architectures machines, eg. Intel Itanium, allowed the programmer to arrange many instructions to be dispatched simultaneously. That must be great for performance right? In theory, perhaps, in practice there are so many dependencies between instructions that not all instruction slots in a VLIW could be filled. You end up with silicon doing nothing there. Even worse is the fact that it's very hard fro the human programmer to reason about all this and it turned out compilers were very bad at it as well. These machines did not reach their potential, were hard to program, and failed.

  1. Similar can be said of the Intel i860. A revolution for for Intel at the time, being a RISC processor totally different to x86. In the i860 you could write integer and float ops to run at the same time (You actually wrote two columns of assembler side by side!) The float path could did the maths, the integer path could manage the loop overheads. Not only that the pipeline was explicitly visible an managed by the programmer. Get this: when you wrote an instruction the result it produced would not go to the destination register you specified there, thanks to the pipeline the result would go to the destination register specified in an instruction 4 lines down the page!

Turned out, again, that nobody could program the i860 for peak performance, human or compiler. It was just too hard.

  1. That enterprising human, having spent an huge about of time and effort creating the optimal code for the processor he is using would be distressed to find that he has actually deoptimized it for other x86 processors that have different caches, branch predictors, parallel dispatch limits, what ever new feature Intel dreams up next. The optimal way to write code, the very instructions to select, has been changing over time.

  2. Creating optimal code execution cannot be done at compile time. With branch predictors and such performance depends on the data passing through, which is not know when you write the code.

I believe this why the C style abstract machine/programming model has won out. All that tricky piplining, instruction scheduling, speculative execution, caching etc going on in the processor underneath saves the programmer from having to reason about those details and importantly can do it optimally because it does it at the right time, when the program flow is actually known as it runs.

1 Like

We're way off topic now. :slight_smile: But to add a counterexample, OpenCL and CUDA are languages used in practice that take parallelization to the extreme. I'm not claiming that I have any examples that utilize hardware to the absolute fullest extent possible. Just that these languages fit the model of the hardware far more closely than C fits the model of the x86_64 architecture.

2 Likes

They are good counter examples.

Problem is that both the hardware there and the languages designed to drive it are designed to cater for doing hundreds, thousands, millions, of the same thing at the same time, over and over again.

It's not clear that helps at all with the bulk of general purpose code we have to deal with. Many algorithms are inherently not parallelizable to that extent.

A web server in CUDA anyone ?

2 Likes

I believe we're completely in agreement. No one wants to build a web server in assembly, either. It is not practical.


To the original topic, I am tempted to argue that learning C++ before Rust is the opposite order for anyone who wants to learn both languages. The reason I believe this is because each language will teach the developer the same lessons; how to manage access to resources, how to translate logic into executable code, how to gain performance by parallelizing multiple independent tasks. But the experience gap required to learn these things is enormous between the two languages.

The main difference is that Rust is very interested in helping you learn these lessons correctly and quickly, while C++ will practically ignore the correctness of the code. It is not unusual for memory safety bugs to lie dormant in C and C++ code for decades before they are discovered and shown to have security implications: ref-1, ref-2, ref-3.

Because these types of problems are invisible, the developer is not given a chance to learn from them. A developer who learns Rust first will have a very different experience as the compiler guides them through a treasure hunt, avoiding the traps and pitfalls along the way. "Don't step on that rock", the compiler says confidently, and the trap isn't sprung. The developer learns by example what to avoid. Sometimes learning how to avoid the traps is what we usually hear described as Rust's "learning curve".

After going through the adventure with Rust as a guide, the developer then picks up C++ and will be carrying a powerful secret weapon with them; the ability to sense the traps and pitfalls. They will still have to learn how to navigate in the new world of C++, but they will be less likely to create security bugs that go unnoticed for 20 years.

4 Likes