This is the first of two posts on Rust. Here I will explain the basics of my Rust experience and the next one, we will dive into some of the really great details of Rust. Like a lot of developers, I felt like I had to give Rust a try. At work, I was wondering how easy it might be to build an image color analyzer. Long story short, I thought it might be easier. This thing has a real problem with the color brown. Anyways, let's get back to my experience on this project. I really wanted to give Rust a try with a real world project, because I thought it would help me learn quite a bit, and I was not wrong. I had played with Rust a while back, and read the the book previously, like year(s) back?. It's clear that Rust didn't fully click for me then, and I banged my head against the wall several times on this project as well. But looking back, I think it's worth it, and that might be the only way to grok all of it. Here's what I learned along the way, and I can only hope that it encourages you to do the same. All of the code for the Image Analyzer lives here if you would like to check it out for yourself.
Cargo is the de-facto package manager for Rust. It can be used to install crates, manage dependencies, start projects, and even publish libraries. For the most part, it's use was an absolute pleasure. I only bring it up for one reason. As is to be expected, Rust is still maturing, and with that you will be sure to run across a number of Rust features that are only available on the latest nightly distribution. Cargo can handle different versions, so just remember to use cargo +nightly when needed. Much of the aforementioned image color analyer repository requires Rust nightly.
I used quite a few crates getting started with this project. The most obvious and heaviest lifting of the bunch would have to be . I cannot speak highly enough of this package. Turning an image URL or file into an RGB image could not have been easier. So, for my first attempt at color analysis, it was as simple as looping through the image iterator and measuring the pixels. The real joy in using rust crates is with the documentation. It is so complete and so easy to find, it really makes the learning curve so much easier. See for yourself how easy it is to take an image from a URL and start analyzing the pixels.
The "2nd most" important package was the web framework Rocket. This was slightly less enjoyable, but for good reason. Digging into the rocket framework, you can really pick up on the fact that Rust is still in its infancy as a language. The API changes considerably (seemingly nightly), and it still does not have a great way to parse multipart/form-upload content. However, doing the parsing with the multipart library was not too bad, and it sure helped me get more comfortable. And of course, of huge note, this package is still in infancy. Rocket v0.3 is very incompatible with v0.4. Make sure you're focusing on the correct version of documentation, and filter those Google results to the more recent.
Macros are most explicitly a meta-programming tool for code generation (examples can be found here or here). I mention them not because I have a personal liking for them, but because they are everywhere when getting started. We will dig into them much more in the next post, but for now it's just useful to understand what they are. Below is an example of creating a route with rocket and an attribute macro. And then below that is a derive attribute which uses a procedural macro to generate a serialization method for a user-defined struct.
I have previously mentioned concurrency in Rust, so I will choose not to do that elaborate too much here. However, the image analyzer runs a few (slightly different) color prediction algorithms concurrently. Understanding how Rust achieves its concurrency correctness is wonderful, because it almost feels as if once the code has compiled, it is correct. And, of course, running the 4 algorithm variations concurrently did run at a speed so fast, there had to be a bug....
Testing in rust is very nice. It is built into the language in many ways. The concepts of unit and integration test seem almost strangely native to the Rust crate design. For example, running a unit test could not get any easier. The most awesome way is to include it as a comment for your function.
And then when it comes to integration tests, it feels just as natural. Integration tests live outside the crate, so (of course), they import the crate and call the module directly. This means that integration tests must call a public API, the way anybody else would. Libraries are exposed by default through a lib.rs file, and those are, of course, the ones that would require integration tests.
Overall, Rust is truly an exercise in delayed gratification. Taking on a new project in Rust was not the most enjoyable experience. Rust is very much a paradigm shift, and it takes some time to beat the borrow checker. The compiler errors can seem never-ending at times (at least for me), but at some point it will click, and you will start to understand object lifetimes, and why you can't do all the things you do in other languages. At this point, it will all be worthwhile. I still have quite a bit to learn, but it has been a great experience. And of course, it would be a travesty for me not to mention them again in this post.; the docs for Rust code are so great. Visit docs.rs early and often, because it is a great experience.