A Long Goodbye to C and C++, And Hello To A Rusty Future

My first serious programming language was C. Before this, I had copied BASIC code from computer magazines, and was hooked at the power of…

A Long Goodbye to C and C++, And Hello To A Rusty Future

My first serious programming language was C. Before this, I had copied BASIC code from computer magazines, and was hooked at the power of software. But, it was C that has become my native language.

So what about the mighty Python?

To me, Python has a crazy structure and syntax that I have to continually recap. While I know how important Python is, I often really dislike using it, and would also avoid for anything that needs to be built at scale. While fine for small programs and small scale scripting, it has real scalability issues.

And, so, as C was my first proper coding language, and it will never leave me. But most things I do in coding still go back to my C roots. And, the move to C++ just passed me by, but I know how important it is to the industry.

These days, I spend a good deal of my time developing cryptographic prototypes for research work with C, JavaScript, Python, and Golang, but when I push into production, it is mainly Rust code that I produce. Basically, I often start with a given cryptographic library, and then use the code that integrates with the methods I need. But, then, I often rebuild again with Rust. The early prototypes give me a benchmark, and a testing infrastructure that I can check my Rust code against.

Overall, though, C and C++ have served our world well, and have allowed us to build our existing systems and software. But, it comes from an old world, and where we could do just about anything we want with the code, and get away with it. The C compiler likes to grumble if we use a pointer to a string and then use as a byte array, but it just doesn’t get involved with all the fancy checking of the conversion.

Basically, if I want to run off the end of an allocated array and into other memory locations, I should be allowed to do it, as maybe I want to do it. But, software is now too important in our world to let programmers do whatever they want with their code.

The “do anything you want” programming language

And, so, for those experienced in C, they can perform fancy tricks, but for the rest, C leads a coder into areas that just leave them in freefall. The greatest problem is the non-checking of the size of a data structure in memory. For example, if I want an array of 15 integers I define:

int myarray[15];

and that goes from 0 to 14, so if a coder accesses myarray[15], we go into another area of memory that was allocated to us. This is a standard buffer overflow. But, what is myarray? Well, it is just a simple pointer to memory. As we have integer values, the code knows that the first element is four bytes away from the first integer, and so on.

In its purest form, we can just allocate it as a pointer:

myarray = (int *) malloc(15);

This allocates 15 byte memory locations, and where the pointer is just the first memory location. We are then casting these to integer values (and which take up four bytes in memory). If we actually wanted 15 integers we would implement:

myarray = (int *) malloc(4*15);

But, what if a system implemented integers with more than four bytes? Well, we could implement a size_of() method:

myarray = (int *) malloc(sizeof(int)*15);

And, to confuse things even more, when we access the integers with start with myarray[0] and end with myarray[14]. If we pass our array into a function, we just pass the pointer, and let the function do whatever it wants with the memory allocation:

void dowhateveryouwantwithmyarray(int *a)
{
/// go crazy with a
}
main() {
myarray = (int *) malloc(15);
dowhateveryouwantwithmyarray(a)
}

I remember teaching C programming, and everything was great, until Week 7. It was then that I taught pointers and passing parameters, and most of the students struggled with the terseness of the code, and the lack of checking of the variables passed. And, of course, a string is just a character array, and where we might allocate 20-byte locations for a string, but where the user could enter more characters than this, and overwrite memory locations that were not allocated to it.

Goodbye C and C++ (for new projects)?

Basically, C and C++ come from a simpler time. It was coding for engineers to do whatever they wanted with the code. And, so, now, Mark Russinovich — the chief technology officer of Microsoft Azure — has said that developers should stop using C and C++ and focus on Rust. Why, well, mainly because of security and reliability issues. C and C++ really can’t be trusted.

Rust, itself, has come a long way in a short time. In fact, its first version was released in 2020, and created at Mozilla. It is now even finding its way into the Linux kernel (including AOSP — Android Open Source Project):

As with C and C++, it produces native code and is thus fast as compared with the interpreted languages of JavaScript and Python, or the language that run in a framework, with as Java and .NET. Rust is trusted not to run in a managed environment but can run native on the machine. For critical applications, such as in kernel integration, this must be highly trustworthy. And, so, Rust is memory safe, and makes sure that all of the access to memory are checked at the gate — and the gate is the compilation process rather than at run time.

Most languages thus do not manage memory well and end up with allocated memory that never gets cleared up. For this, we need a special task — known as a garbage collector, and which analyses the program as it runs, and gets rid of the data that is not being used. This can create a significant overhead on the system, and just because of coders not unallocating data structures when they had finished with them. Rust, says “No!”, and where you only use data for as long as you need it. Unfortunately, we have all been taught how to create data wherever we want it, and just forget about that data when we don’t want it anymore. We thus let the garbage collector do the work for us.

Why?

But, what is the evidence? Well, recently it was found that around 70% of all the memory bugs can be sourced to C and C++, and around the same level of bugs in Chrome can be traced to C and C++. Mark Russinovich, in fact, says that C and C++ should be deprecated, and eventually removed for the building of software systems. But, it is still one of the most taught programming languages and is still one of the most in-demand languages, so something perhaps needs to change at a fundamental level to move forward the software industry.

Along with Microsoft moving its code to Rust, Meta and AWS have also been moving back-end code (server side) to Rust, while other companies are looking to add-on memory-safe checking options into the C++ code. But, overall, there is too much invested in C++ to move away from it at the current time, and it is likely to take decades for us to see the last of C++.

Rust Never Sleeps

Unlike Python, Java and Node.js, Rust compiles to a binary code and does not need a run time environment like .NET/C# and Java. For this, it has a “borrow checking” method and where a developer goes not have to release used-up memory — the compiler does it. It thus avoids the C programming nightmare of memory overruns and underruns (and is memory safe) — no memory leakage. Along with this, it supports concurrent operations. Overall, Rust runs as fast as C, and faster in many applications. At the core of this, is the ability for the Rust compiler to code directly to native machine code, and thus optimize the operations. This leads to not having to worry about garbage collection, as we do with many other programming languages.

Cargo

This speed and security come at a cost … as the compiler is doing so many additional checks, there is a steep learning curve in learning the language. But, once you are there, there’s an amazing toolchain to support the integration of virtually every library you can think of, and there’s even a package manager: Cargo. You basically create a project with “cargo new projectname”, and then add your Rust code (with an rs extension) to the src subfolder. Add a TOML file to define the crates you want to use, and then issue “cargo build”, and it builds it for you. Then to run, it’s just “cargo run”, or find the EXE, and you are off and running.

And Environment?

So what about coding? Well, if you like light-weight environments, there’s the wonderful Microsoft Visual Studio Code, or the mighty Microsoft Visual Studio. There are lots of add-ins, and highlighters to help you with your code:

The CLI to build a CLI

Rust is — perhaps — the language that you build core tools, as it is so trustworthy. So building operating systems, compilers, and developing frameworks are all perfect for a language that checks things at the gate and makes sure there’s no updated version of libraries that can slip in additional code. As with .NET/C# it is perfect for creating CLI (Command Line Interface) programs, which are so loved by developers.

And Rust, of course, is built for the Web and supports the integration of HTML, CSS and JavaScript. For this, it supports WebAssembly so that Web integration is well supported, and in a secure manner.

But, a key focus is to make developers think of about what they actually need to do. They can’t just be sloppy in passing any old variable into methods; they have to be very clear from the start how this is done, as there is much more enforcement of these things by the compiler. This overcomes many of the problems in other languages, especially in the usage of pointers in C, and in the casting of anything to anything else.

Conclusions

The key tipping point for Rust, will be the availability of useful libraries, and the requirement to rebuild back-end infrastructure with something that doesn’t just cobble together a whole lot of dissparate pieces of code. I can see now, that experienced researchers and coders, present their methods using Rust, where at one time C and C++ was the only way to outline new methods.

While Golang is my go-to language for cryptography prototyping, it is Rust that I turn to in creating real code. One slip in cryptography — a single bit in error, even — can bring a whole system crashing down. Cryptography is all about trust, and it needs trust in the way that the code runs on the back-end. While Rust doesn’t really cope well at the front-end, it is there to provide concrete foundations at the back-end.

So, I need that robustness, and where the compiler slaps my hand every time I forget about a variable that I don’t need anymore. If I had been taught this when I learnt C, I won’t be doing it now. And, so, just because you use Rust, you don’t become an amazingly secure developer, as you are still likely to be using all those bad habits and trying to find ways around the compiler. We need a new approach to developing systems and forget about our past. Let’s build code that is secure and resilient by design!

If you are into cybersecurity — and everyone should be — here are a few Rust examples:

https://asecuritysite.com/rust/

Because Rust Never Sleeps!