A personal journey to learn Rust by jumping in the deep end and writing code
I decided to find out what the deal is with the Rust programming language. It's said to be as performant as C/C++ (henceforth just C for short) but also safer. So I took the plunge off the deep end and started rewriting in Rust a small non-spooling printer server of mine from many years ago. This is by no means a tutorial in Rust; there are resources for that and I'm still learning. It will be a collection of factlets I glean that give insights to the design philosophy. I will update this page, correcting mistakes as I become cognizant of new facts, unlike so many public figures who just double down. 😉 I may also reorganise sections when OCD strikes.
Rust is not like C
Don't believe the people who tell you Rust is like C. It doesn't look like C. It looks more like ML.
Rust is like C
However Rust is still an imperative language. Many of the constructs map onto a small number of machine instructions, like C. Rust gives you access to low level operations. Word sizes of various lengths are supported and conversion between different sizes is controlled.
Rust is a typed language with type inference
But often an explcit declaration is not required, though allowed, because Rust can infer the type of many objects. Even numeric constants are typed and checked in expressions, unlike C where promotion happens and may or may not provoke a warning from the compiler.
Mutables are discouraged
Part of the safety of Rust comes from discouraging variables which are altered after being set. Instead one elaborates the computation by deriving new values as execution progresses. So a program ought to look more like a sequence of mathematical statements rather than instructions to poke and peek pigeon-holes in RAM. This mindset permeates programming style in Rust.
Does this affect compiler optimisation? It might even improve it since the compiler doesn't have to map intermediate values to memory locations but can store them in registers and other temporary locations as it sees fit.
But in the real world some variables must change, think I/O, so mutables are available.
Functions can return complex objects
It's not well-known but C does support returning complex objects like structs. This isn't used much, as programmers prefer to work with pointers or the safer references. In the beginning only objects that could fit into a register were returned, so this led to overloading system and library calls to return -1 or 0 for failure. In Rust this isn't done. Functions can return a complex object. Typically this is an enum. Which brings us to:
Enums are not enums as you know them in C
They are more like tagged unions some of you may remember from Pascal, Ada and Modula-2 as discriminated unions. variant records, or a similar name. So typically a function returns an enum consisting of a union which contains either the result on success, or details of the error on error. As the return type is checked strictly at assignment and all variants of the enum must be covered, this hinders programmers from ignoring the 6th Commandment of C Programming by Henry Spencer.
Rust is primarily an expression language
Even constructs that are statements in C, like loops, return a value. This neatly solves some problems. An example is where you search in a loop for the index of a match in an array. In C you have to note the index of the match then break out of the loop. So you have to make the scope of the index variable wider than the loop, mutate its value, and also test for no match. In Rust you combine a let with a while or loop, and the break returns the index of the match. Failing to find a match would return the failure variant of the enum. No scoping problem, no mutable needed.
In Rust the last expression evaluated before exiting the function is the return value....