My Journey Learning Rust
I've been meaning to learn Rust for a while now. I never got into C or C++ as, frankly, memory management scared the hell out of me. I only got into Objective-C for a brief stint during University because of the advent of Automatic Reference Counting - without that crutch I was mostly useless.
The Carcinisation Of My Brain
Rust definitely works a lot differently to what I'm used to - there's no inheritance at all, only traits, and there is no traditional try/catch behaviour. My brain has wired itself strictly to think in terms of hierarchical inheritance, so it took a while for me to organise my brain about how things work.
Another, small thing that caught me out was I was shocked at the binary size, even a minimal "Hello, world!" program was well over a megabyte in size. After a bit of searching I realised that, by default, Rust links things statically - meaning all of the dependencies in the application are built in, even libc. This solves a "dependency hell" problem when distributing the final application, but it can be switched off.
Something I didn't think would immediately "click" in my head is the way that Rust handles memory management. There is no garbage collector, there is no memory management, there isn't a way to traditionally "leak" memory. Rust enforces this by adding the concept of "ownership" to pieces of memory, that is, structs & enums. There are two parts to this:
You can "borrow" a reference to memory, either read only or mutably. There can be multiple simultaneous borrows, providing they are read only, but you can only have a single borrow if it is a mutable reference. This provides built-in memory safety, guaranteed by the compiler.
If you pass a memory reference without it being a borrow, the ownership gets transferred to new variable (either an argument or another variable), and the variable which originally contained the reference becomes invalid. Passing around the reference like this means that eventually the reference will fall out of scope somewhere, and then it will be deallocated. No garbage collector, no reference counting, no manual free() calls.
The developer experience of using Rust is, frankly, phenomenal. The Rustonomicon is a fantastic reference guide with detail on how the underlying code works and Rust By Example provides a library of example code on each topic to allow one to see how things work in practice. This combination of theory and practical documentation is excellent.
The elephant in the room though is the compiler and language server. I elected to use Visual Studio Code, using the recommended development container for Rust. This started up with everything I would need for development installed and pre-configured. Without having to do anything, whenever I made a boo-boo in my code, the language server was ready to go to show me exactly where I went wrong without having to invoke cargo build.
However, this only really works if the code you are looking at is affected by your changes. Thankfully the compiler errors are equally helpful, providing detailed reports on what went wrong, pointing out the actual parts of the line affected instead of line and column numbers. Best of all, it provides suggestions for what you can do to fix the issue!
While the suggestions are not perfect 100% of the time, they provide so much context to the error and a great starting point for how to proceed further. It stops my brain from getting stuck on the issue and gets me immediately back on track.
Rust seems, so far, to be living up to its promises of terrifyingly performant code with easy, inherent memory safety.
Editorial note: This used to be a much longer post, talking about the project I have started writing to learn Rust properly. However, for clarity, and to make sure I actually publish something, I've split it into two posts. Go have a look at the second part here.
||22 Nov 2022|