The COVID19 lockdown has been a pretty challenging time for everyone. Staying sane without the social interactions we normally have can be difficult. I took the time to learn a new programming language. This served several purposes.
Additionally, there are other considerations in learning Rust:
Working in a world full of kubernetes, I’ve come to appreciate the small download size of compiled languages like Rust, Go and C/C++ over what feels like the bloat of JIT’d languages, and scripted languages that all need to carry around an immense amount of machinery just to provide a runtime for apps written with these languages.
Startup times: Compiled languages have it hands down here. Equivalent programs in a compiled language compared to a JIT’d language are significantly faster at delivering the first byte. Again, in a kubernetes world, this can have significant advantage.
Thinking ethically, the fewer bytes we transfer and the less work we need to do at runtime, the less energy we use. This is a win for the planet and a win for your pocket. Leaner applications using less memory, and taking fewer CPU cycles, mean you need fewer resources to run those apps. For a single app, that might not mean a lot, but if you are running tens or hundreds of them, that can make a big difference to the impact on your wallet.
So how long did my journey take? I spent about two months dabbling with a toy project, plus any number of attempts to read the The Rust Programming Language. The process is ongoing though. I’m no where near entirely proficient with the language, albeit that I feel significantly more confident than I did at the start of this exercise.
My toy project; Uploader - An application to watch a directory and move the files to cloud storage. When I first thought this might be a good project with applicability to services that we write at FINBOURNE, I immediately thought of Go. In searching around though, I couldn’t find a good “file watching library” that would also watch subdirectories. So I changed tack; What if I used this to help me learn Rust? After all, I learn best while doing; the exploratory process helping to frame the unknowns of the problem domain. Lots of pre-reading doesn’t stick unless I’m also doing. Little did I realize that this project would turn out quite well as a learning exercise. It managed to hit on the following points of interest:
Now to be fair, many of those touch points would have been covered with a contrived learning application, but I was surprised at the breadth it managed to cover. And it also manages to be a useful program, with real world application. Though it may never see service as I intended it, it has served its purpose in helping me learn Rust. I could have written it in C# in a couple of hours; but I’d have missed out on this golden learning opportunity.
Here are the challenges I found to be most prominent while on my journey:
Libraries
Like most languages, learning the ecosystem is challenging. It’s not that I didn’t know how to do what I wanted to do, it’s that I didn’t have any experience with the libraries that would help me achieve that goal. Learning to use libraries that have a slightly different bent than those I have been used to (many years as a C# dev) takes time. They also incorporate all the additional type metadata that Rust requires; ownership rules. Not to mention that in many cases, there are multiple libraries to choose from. Which one is the best? For what definition of best? It can drive a dev to distraction sometimes. So that’s challenge number one.
Trusting the compiler
Really!
rustc
and cargo
are seriously good at telling you how to fix issues they identify. In C# or Go land, I’d have seen an error and dove into the code to try and figure out what was wrong, make a change, compile, wash and repeat until it’s right. cargo build
gives you a monstrous amount of help. I spent probably a month not able to break the old habit. I couldn’t help but dive into the code and start tinkering. This led me down rabbit holes of code change to try and satisfy an error that, when I finally rolled back 10’s or 100’s of lines of code because I frankly just didn’t know what I was doing, was a very simple change, and the compiler told me what to do. It’s a different way of working, and a shining light in Rust development.Documentation
Just learning how to read the Rust documentation for libraries is a challenge and skill unto itself. To be fair, I’m probably hampered by years of barely having to look at library docs to figure stuff out, because the IDE support for code completions is quite exceptional in Visual Studio. Add in Resharper, and you barely need to go to the internet for docs. Couple that with years of understanding the ecosystem, and I could always make educated guesses as to what methods I needed to call.
Ownership
It’s a simple concept. It is also difficult to do away with 25+ years of programming habit. Not having to think about ownership of data is the mainstay for all other programming languages. Suddenly add it into the mix, and it takes a little while to get used to. Does the function you are calling take ownership of the data? Do you need to use it after that function call? Copy/Clone!! Not using it, pass it in and forget about it. Borrowing? Just add
&
! Seems simple enough, but until it becomes muscle memory in your programming, it can create a lot of issues. Not to mention the whole lifetime thing that means you sometimes just have to create a local variable that you then pass in to your function. This bogged me down any number of times, and was the main cause of my divergent coding frenzies.
They were the main challenges, but there were others. Getting a good IDE setup took time. That is until a colleague told me about Rust Analyzer. Friends don’t let friends code Rust without Rust Analyzer. Thank you to the maintainers, it’s a fabulous piece of kit.
Where do I start? So many things to mention. I started writing a mind map, thinking it would be nice and simple. But eventually a little spider turned into a web. I’ll introduce you to the things I think are the stand outs. At least from my limited experience.
Result<T, E>
. There are no exceptions to be caught, just what is basically an either
type. You have either, a result, Ok(T)
or you have an error Err(E)
. Add the questionmark operator for automatically unwrapping `Result`s and you have a pretty powerful and thorough error handling solution. Then there’s panic
but no exception handling. You can’t throw, and there’s no try/catch
`. panic
really does do what it says.Option<T>
either has some data; Some(T)
;or no data; None
. This results in not even a hint of the concept of a NULL.That’s the headline stuff, but there’s also memory allocation/deallocation, stack allocation by default, the macro system, and smart pointers. These are just the things that I managed to come across when writing Uploader. The language is so full of interesting features you can quickly get lost. But I’d recommend you persist. Conquering a Rust program can feel pretty rewarding when you get that nice green, unencumbered build success.
Of course there’s also the build tools,
cargo
, clippy
, rustfmt
, and the fabulous Rust Analyzer. Using these tools made building Uploader quite a joy, for all the hand wringing I did over things I didn’t understand at the time. In fact, with that combo, every time I managed to get a flawless build, I felt pretty confident that I had a working product; to my surprise, every time.I had Rust on my to-learn list for so long, that when a viable use case for it became available, along with a viable amount of time, I jumped at it. Lockdown has saved me roughly three and a half hours of travel time per day. That’s a lot of extra sleep, a lot of extra family time, and a lot of reclaimed time to learn something new.
I haven’t finished with Rust yet. I’ve got a lot to learn still, and I genuinely found working with the language to be enjoyable, and refreshing. I plan to start a little coding club at work, and find small, non-critical use cases for Rust that will aid the learning for all those involved. Who knows, maybe one of them will be used one day in a real live production system. Wish me luck.