Getting Rusty

April 29, 2022

I'm hearing more and more about Rust. I began hearing about it while finishing engineering school, when my local Robotic engineering club was poking around and putting Rust in its embedded computers on robots (I think it was pretty ambitious at the time, kudos to the team). Now, Rust is propagating everywhere, from CLI tools to web applications, and even the Linux kernel is now getting some Rust. Seeing all the craze about the language, all the "blazing fast" and "reliable" softwares appearing with this language, I wanted to take a look at it and make my own opinion about it. More generally, I see two main reasons to take a look at it :

  • I feel that Rust is becoming something big in software development. Taking only a look at its adoption level, the software development community seems to be unanimous about the language (sure, there are some detractors, but that's true for everything).
  • as a software engineer and as a hacker, I must keep learning new technologies, to maintain my skills the pleasure I take in working on projects. As of today, C is my daily driver, and except so tooling in Bash or Python, I do not know many other languages, so Rust may be a good target.

So here is how I have dived into Rust !

Disclaimer : the rest of the article only expresses my experience while discovering Rust

Why Rust

One important goal for me when discovering Rust was to convince myself about the advantages of Rust. Sure, you can find many sources which list the features of the language, but I needed "to capture the concepts", to get real examples of why it could be better than my current tools. After experimenting with it, I am now convinced about its benefits, and here are the main advantages I see today :

  • memory management : Rust has no garbage collector. Instead, it relies on monitoring data ownership and scopes to check if data is still in use. Moreover, it prevents undesired "side effects" when sharing data amongst multiple modules, by establishing "borrow" rules and lifetimes. I will not lie, I found it quite difficult at the beginning and I am still learning to properly work with those rules, but I now see the avoided burdens when executing similar tasks in C.
  • error management : tired of dealing manually with errors returned by APIs, nullable values, pointers initialized or not ? Rust comes with built-in data structures and mecanisms for all of that. (see Result<T>, Option<T>, pattern matching, etc).
  • polymorphism : while POO developers seem to be sometimes disappointed by Rust, as a C developper I find it quite satisfying. Rust is not object oriented if you follow the traditional definition. However, it provides basic polymorphism genericity through Traits, which is an interface mecanism, and Generics
  • tooling : getting started with Rust has been made pretty straightforward. You install rustup, which allows you to install very simply your compiler for desired target. It comes with cargo, the swiss army knife of Rust development. Many other tools come around, like clippy, rust-analyzer or rustfmt.
  • compiler : one big change from C is that rustc compiler detects many design errors right at build time (the memory management point above largely benefits from it), instead of runtime. It may comes at cost of larger build time, but the gain is surely worth it. Moreover, rustc (the compiler) is generally very explicit about errors causes, and even educative about it.
  • crates : Rust libraries, or "crates", are shared on a central registry called crates.io. cargo allows to pull and track all your dependencies with crates.io
  • documentation : Rust ecosystem seems to have established a high standard on documentation. I feel that many crates still lack of example code, but generally speaking the documentation is pretty good, especially for official general documentation (example : Rust Book)
  • testing included in ecosystem : Rust has been designed with TDD in back of the mind. You can execute your red-green-refactor cycle with basic tools such as cargo, without setting up external dependencies

Different flavors of Rust

Learning Rust is a thing, applying it on a concrete project is another. As a consequence, and because of my embedded engineer background, I have felt quite overwhelmed by the quantity of topics to learn to be able to use Rust for projects similar to professional projects I work on. In retrospect, I feel that, for embedded application, Rust learning process can be divided into small steps. Taking shortcuts and trying to reach the final target looks like a mistake on the long run : you can miss some core concepts or mix some elements without really knowing what you are doing. Based on my experience, I would advise the following schedule :

Learn "standard" Rust

By standard, I mean learning by developing an application or a tool meant to be run on standard computer, relying on Rust standard library (std). For this purpose, Rust Book is the perfect entry point. It takes time to explain all the core principles of Rust language

Learn embedded Rust for Linux

Now that you now how to develop basic applications with Rust, time to try to cross-compile an application for a Single Board Computer (SBC) like a Raspberry Pi. That is not a big step compared to the previous one, but it involves project and toolchain configuration, as well as some tooling to keep the development process smooth. Moreover, it allows to start taking a look at available crates on crates.io to start dealing with some hardware.

Learn embedded Rust for bare-metal

Time to dive in what I would call the "hard side of Rust" : applications running with the notorious "#![no_std]" feature. Since the beginning of the learning process, applications have relied on std, which in turn is based on features provided by underlying operating system. As a consequence, bare-metal applications can not rely on std. Instead, we use the "core" library. Such applications need to be build with a specific layout to be able to boot : you have to define a memory layout, an entry point, define the default panic behaviour... and so on. Moreover, dynamic allocation is by default absent (because "core" does not provide heap allocation), so you do not have access to cool features like all types implementing the `Clone` traits, the smart pointers, etc. You either need to work with variables allocated on stack only, or to declare/use custom allocators. This is this step which made me think that it is important to properly separate the learning steps in Rust, to properly understand what features are "general" in Rust, and what are the specificities linked to no_std applications.

Learn to sprinkle Rust in existing codebase

Finally, time to bear with one important constraint of (embedded) development in current industry context, especially when you try to use a young technology. Most of the time, you will rely on SDKs (Software Development Kit) and/or libraries distributed by a industrial partner, specific to the processor or a hardware/software component you are using. You will not be able to develop your application without this provided software, because it provides core functionalities to your application : connectivity, video, or even board support package... Let's face it : such provided software will not be in Rust. It will mostly be provided in C, as it has been for many years in embedded development. Maybe some Rust alternatives will exist for largely used processors/components, but it is probably not the best solution to use if you want to keep support from your provider. Fortunately, Rust provides a mechanism which allows to call C in Rust, named Foreign Function Interface (FFI). While it makes it possible to use C libraries in Rust, it requires quite a big amount of work to make it possible. Rust provides tools to ease the process, like bindgen, but there is a big part of manual work to make the C code comply with Rust mechanisms. Indeed, since C code does not allow Rust compiler to enfore all its checks at build time, you can not call directly C code from Rust. Instead, you will have to start flirting with unsafe Rust, which is  a Rust language inside Rust language, to proceed : you will have to develop "safe" wrapper around your C code, in which you will ensure yourself (instead of letting the compiler do it) that all calls and data processing are done safely. My feel here is the following : when finally reaching this point, pros and cons of Rust must be properly evaluated. If it becomes too cumbersome to develop wrappers around C libraries (because said libraries are way to big, or too numerous), is it time to reconsider the hybrid C/Rust approach ? Would it be better to go full Rust, and if so, are we able to redevelop the full embedded stack ? If so, at what cost ? If it is not possible or acceptable, is it still a good idea to force Rust introduction in this project ?

My learning path

Now that I have introduced my ideal learning steps for Rust, how did I really proceed to learn Rust ? Here are the experiments I have lead to initiate and increase my Rust knowledge.

  • first of all, I have read the Rust Book. This is the de facto introduction to Rust, and is pretty well written. I would advise to take time to properly configure an IDE with all tooling (on my side, a VSCode configuration with rust-analyser, clippy, and basic cargo tasks)
  • before jumping into a concrete project, I felt that I needed to play on properly bounded exercises to make sure I have understood the core concepts. This is where I have found Exercism. It is a programming community aiming to support software development learning in many languages through real mentoring. You can select exercises sorted by languages and difficulty, submit a solution for a specific exercise, and ask for mentoring for a real person to review your solution. All mentors are volunteers, and you can even be a student in a language you are discovering, while being a mentor in your predilection language.
  • Then I found another resources to apply my fresh Rust knowledge : the Advent of Code. During December, the author of this website post a challenge each day, which is mostly a question expected a specific numeric answer. If you are stubborn enough and have a lot of time to loose, you can take a pencil and a sheet of paper, and try to find the solution manually, but the problems are designed to be almost impossible to solve by hand : you need to develop a program to find the solution. I loved three things about this challenge :
    • you are given an example of input and expected output: the goal is obviously to make you work in TDD style. You know your code is valid only when you have some tests proving that it can compute the correct output for provided example inputs.
    • the riddle is personalized for each used : the input is generated for each user, and is way bigger than the example input. So you can not simply copy another challenger answer, because you do not have the same input
    • each day's challenge is in two parts, the second part being hidden while you are still solving the first part, and it can be a real pain if you did take shortcuts in your first part implementation (e.g. : brute-forcing a guess), so it involves design decisions and refactoring skills.

The challenge becomes increasingly difficult as we approach the final day (Decembre 25th). I have been able to reach day 18 before giving up. Those challenges were an awesome way of experimenting TDD in Rust !

My Advent of Progression for 2021. All in Rust !

My Advent Of Code 2021 Progression. All in Rust !

  • Next step has been to develop/port some real tooling in Rust. From this intention is born redge. redge is the Rust port of edge200_exporter, a python tool which allows me to upload my cycling activity from a non-connected GPS to Strava, through its public API. This project allowed me to explore some famous crates to realize common operations : manage files, manage command line parameters, call a REST API, etc. It has been a delight to find and use the appropriate crates and use it with Rust standard patterns !
  • Time to get my hands dirty. Next step has been to try some code onto bare-metal. That is the part where I mixed the few last steps of the previous section of this article, so I have been struggling a bit (okay, a lot). The target was the following : to deploy some Rust code on an almost bare-metal target (it was using an RTOS), while keeping most of the C code. I quite managed to do something, but did not reach far and used most of my time trying to wire C code into Rust code and vice versa. When I realized the mistake, I took some steps back : I pulled out a STM32F4 discovery board gathering dust in a cabinet, and experimented with pure Rust, bare-metal application, playing with peripheral crates, embedded-hal and board crate.

Yes, the good old blinky

Conclusions and next steps

After a few months of play with Rust, I am quite (very) happy with Rust in general. Coming from C, the learning curve can be quite steep at the beginning, but there's a sentiment of power once you start to find and apply development patterns : you create and implement structures that make sense, learn to pass data while keeping the ownership and borrowing rule in mind, propagate errors properly between your layers... However, I find it still difficult to use it in bare-metal projects, especially while thinking about industrial challenges : Rust is still pretty young, so it will not make C disappear in the next years. If we have to deal with manufacturer stacks to make some hardware work, we will have to deal with C and Rust cohabitation, which seems to be hard, maybe to the point of excluding Rust. As a consequence, I feel that the pros and cons must be evaluated carefully before starting an embedded project in Rust, but of course, this is only my opinion after having only poked around a bit with it ;) I guess that the mere existence of dedicated companies like Ferrous Systems proves I still need to explore Rust in embedded to get its true potential.

I want to pursue Rust learning. I am trying to define a cool project where Rust would fit. I think I will take a look at how Rust has been introduced in Linux kernel, and maybe boot a Raspberry Pi with a Rust-capable kernel.


Hello World

April 8, 2022

Hi there. So, I want to give a try at blogging. Why, would you ask ? Because on a daily basis, I read blogs. Quite a lot. I find it to be a marvelous source of high quality content about topics I care about, whether for professional topics or for my hobbies.

And the more I think about it, the more I think that all those people, taking time to write quality content to share knowledge on the internet, put A LOT of effort to produce such content, most of the time for no money. Of course, you can think about exposure, about the positive impact for your career (a blog is of course a nice showcase of your soft and hard skills), but writing (technical) content "for the masses" remains a not-so-easy task. I personally define it as a skill, or maybe and art, or maybe both. On one side, you must master the content : you must have at least a (very) good understanding of the topic you want to talk about, or even master it. But you must also handle the style : being able to shape the information you want to provide, to make it accessible to the readers, can be very difficult. You must give enough background to introduce your point, take time to bring the core information, being careful not to take shortcuts in the "details" (which, sometimes, are not !), putting yourself in the place of the reader, asking yourself if he would understand what you are talking about, while making sure your writing keeps getting interesting enough to keep the reader focus...

So, as a big consumer of such content, I want to try to write too. The attempt has multiple purpose :

  • having an "event log" of my hobbies and experiments : I have some spare projects to work on on my spare time, with more or less complexity, time scope, fun... Having blog to talk about it is a way to record what I have done/tried/failed/adapted
  • share with other hackers and developers about the topics I like
  • improve my skills on technical writing and sharing : being able to share knowledge on a topic is often an underestimated skill, and is pretty important in my professional field. I have given a few talks on tech topics in front of small tech communities before, which gave me good pointers on what can be done better when sharing knowledge, but writing seems to be a valid tool too which I have not played a lot with.

So here comes this blog. I have no target schedule of posting, it will comes as my projects live. Maybe it will work and I will post on monthly (weekly ?) basis, maybe it will be a mere dud and I will stop right away. Let's see where it brings us !