Introduction to Rust and its benefits

A blog by Marwan Hussein, Jakub Valtar, and Hayley Deckers.

Widely beloved among the community that keeps it ticking, Rust has found its way into countless projects made by a diverse set of users, from hobby engineers to large companies like Amazon, Facebook and Google. However, despite the rising popularity, it is still seen by some as a niche language. Particularly in the field of embedded programming and control of physical systems, where a small mistake can have permanent consequences, Rust is yet to gain wider adoption comparable to established languages like C++.

At Fusion Engineering we believe Rust has a bright future as the foundation of tomorrow’s flight controller industry. For the last few years, Rust has been our language of choice not only for our flight controllers, but our entire infrastructure as well. We believe that Rust is the best choice for any new project as it has been our experience that it can provide both performance and reliability, without impeding developer productivity.

In this article we will try to explain why we went with Rust over more established languages and why we are now enthusiastic proponents of the language!

Which languages are suitable for drone development?

In fields like data science, machine learning and enterprise, languages like Python and Java are often preferred. They simplify certain aspects of programming, like memory management, at the cost of not giving the developer full control over the way their program runs and the resources it uses. Contrast this with the fields of embedded and systems development, where C and C++ are the most popular languages. There are several reasons: these are some of the few languages that give the developer full control over managing (often limited) resources, provide high and predictable performance, and support running on almost any hardware, from tiny microcontrollers to supercomputers.

Yet, there is a reason that these languages are not the first choice for projects without stringent performance requirements, as this level of control comes at a cost. C and C++ require the developer to correctly manage all aspects of their program behaviour. Failure to do so can cause mysterious crashes and hard to discover bugs, which contributes to a steep learning curve and poor ergonomics of these languages. They are infamous for how easy it is to make mistakes in them: for illustration, bugs related to manual memory management, one of the hallmarks of C and C++, are said to account for approximately 70% of the high severity security bugs in Chrome (!).

Languages like Python have built-in features, which help you write correct code without having to painstakingly manage resources yourself. Ironically enough, these features are part of the reason why Python is unsuitable for embedded development. In Python’s case, these safety features make it very difficult or impossible to use all the cores of your processor at once. Worse still, your program might randomly pause for several milliseconds to do the bookkeeping, like returning unused memory to the operating system (something which you would have to keep track of and manage yourself in C and C++). This is not a big issue for a web server or a program for processing a batch of data on your computer. For performance-sensitive software, like flight controllers, unpredictable pauses can mean a difference between a stable flight and crashing to the ground after an unexpected gust of wind.

Most programmers would agree that C and C++ gives the developer more control at the cost of ease of use and time needed to verify & debug programs. At the end of the day though, C and C++ are still being used for performance-sensitive software since they are the one and only tool for the job.

Or are they?

Enter Rust

Rust is described by its authors as a “language empowering everyone to build reliable and efficient software.”  By visiting the project website, we learn about the three main reasons to choose Rust for our project: performance, reliability, and productivity. Let’s take a look at each in more detail.

Performance

In terms of performance, Rust is on a comparable level with C and C++. This is good because, in most cases, you can’t do much better than that. All three languages give the developer full control over the resources, the way data is represented in memory, and when and how the program executes. There is nothing taking control from the program to do “bookkeeping” or resource management, so it can perform time-sensitive tasks without worrying about unexpected pauses.

This level of control allows us to use Rust on all levels of our technology stack, from resource-limited microcontrollers to services performing computationally intensive calculations. Having our codebase written in a single language makes it easier to reuse code and integrate all running programs into a robust system.

Reliability

Writing reliable software is a challenge in any language. Rust makes it easier by providing an advanced type system and a unique ownership model, which is not found in other commonly used languages. The type system and ownership model allow us to represent various constraints and have the Rust compiler check them ahead of time, while building our program. If we manage to successfully build our program, the compiler has verified that the constraints are satisfied and we don’t have to worry about them while the program is running.

This is a powerful feature which helps immensely with managing resources. For example, Rust can verify that memory is managed correctly, making whole classes of sneaky memory management bugs, which plague programs written in C and C++, impossible to write in Rust.

Another area where the ownership model has proven invaluable is concurrency. Concurrency allows us to run parts of our program independently, possibly on different cores of the processor, allowing us to do more work in less time. The typical problem with concurrency is that we have to ensure that two parts of the program won’t use the same resource, like a piece of memory or a hardware peripheral, at the same time. If we make a mistake in managing resource access, our concurrent program will contain a hard-to-find bug that happens only occasionally, when some resource happens to be used by two parts of the program at exactly the same moment. Again, the ownership system in Rust can verify that we’re managing these resources correctly, giving us a peace of mind that there won’t be any surprises when running our program.

Productivity and Ergonomics

From our experience, the Rust project puts a lot of emphasis on inclusion, diversity, and allowing everyone to participate in the project. The language is constantly evolving, with a new nightly (experimental) release every day and a new stable release every six weeks. This allows the Rust team to receive feedback from a diverse audience, iterate on the solutions, and make sure the programming language provides a great working experience to a wide range of developers.

The Rust compiler is famous for its helpful error messages, explaining exactly what is going wrong, pointing to relevant pieces of code, linking to documentation and error index with more detailed explanation, and even suggesting fixes to apply. In the majority of cases, you can fix your code straight away, or at least know where to start looking for the solution.

Combined with the advanced type system and ownership model, we can truly rely on the Rust compiler to have our back and prevent us from writing bugs, before we even run our program. Of course, this applies only to certain classes of bugs; Rust can’t tell if we made an error in the logic of our program. However, by making certain classes of bugs impossible to write, it reduces the cognitive load and allows us to spend more time on the design and logic of our software.

In contrast to other languages commonly used for systems programming, Rust comes bundled with a set of tools which every development team needs. It is shipped with a code formatter that ensures the code has a consistent style, a test framework that allows writing tests right next to the code, a documentation generator that creates a webpage with all the important pieces of documentation, and the package manager (Cargo) that allows us to easily reuse existing code written by other developers. The ecosystem created by these tools saves us time and allows us to focus our effort on the area where we intend to innovate: flight control.

To better understand how Rust just makes sense through the eyes of a coder, we spoke to Jakub and Hayley, two of our best software engineers.

Marwan: “What do all these features Rust has to offer mean to me as a programmer and how exactly do they make my life easier? Can you also tell me a bit about how all of this relates to drone flight?”

Hayley: “Generally speaking there’s only a few programming languages that are truly suited for controlling hardware in (soft-)real time.

Languages like Java or Python aren’t really made for that kind of thing because, for example, once every while they will run a garbage collector to free up old memory. In order to do that they have to pause the entire program for an unpredictable amount of time. and, well, that’s not ideal if you’re flying a drone…

Now sure some people will say there’s ways around that but, use the right tools for the job I say. And in that case that’s usually something like C, C++ or Rust.

Then why Rust and not C++? well aside from Rust being more pleasant to work with in my opinion, Rust also gives you safety guarantees that C++ can’t give you; That garbage collector I mentioned before? that has to run because those languages manage memory allocation and lifetimes for you. Automatically freeing variables when they’re no longer used and keeping them around as long as you’re still using them.

In a language like C or C++ you have to manage your own memory, and while modern coding standards make that a bit easier, human error can still occur. And what does ‘human error’ look like when you mess up your memory management? An unpredictably malfunctioning drone.

You can still write a malfunctioning drone in Rust, sure. But if you’re using Rust (and no “unsafe” blocks) it won’t be malfunctioning due to memory-safety issues, because Rust tracks those at compile time for you. Giving you the same kind of safety you would get with a managed language like Python, without the associated overhead.”

Jakub: “Rust is by default safe, meaning that it is not possible to access invalid memory, create data races in parallel code, or get any other kinds of undefined behavior. I don’t have to worry about whole classes of hard-to-discover bugs, which are possible to introduce in languages like C and C++ by making a typo. I can trust the Rust compiler to do this work for me, which reduces my cognitive load and allows me to spend more brainpower on the real problems I’m trying to solve with my code.

The Rust compiler has some of the best error messages I’ve seen. They explain what the issue is, suggest changes to code to fix the problem, and include links to documentation with examples and longer explanation. When something doesn’t work, I can quickly pinpoint and fix the problem.

Rust is a great fit for drones. It compiles to native code and doesn’t use garbage collection, which gives it a predictable execution and high performance comparable to C and C++. Thanks to the safety guarantees made by the compiler and built-in testing infrastructure, it is easier to eliminate bugs during development and prevent failures during flight caused by programming errors.”

Sharing a similar view to Jakub and Hayley, many large corporations are also using Rust as their translator of choice. Who else believes in Rust?

Having gained reputation for emphasizing performance and correctness, it has found its way into applications involving game development, virtual reality, web development, and data science to name a few.

Some of the companies you probably didn’t know are using Rust include:

Amazon for Cloud management,

Google as its Android development programming language of choice,

Microsoft for systems programming on the Windows OS,

and Mozilla as one of its biggest endorsers, almost halving the number of their system’s lines of code by switching to Rust.

So, what is our own take on all of the above?

Before using Rust, our flight controller was written in C++. Given the steep learning curve and the amount of gotchas one has to watch out for while writing C++, it was difficult for our team members without a software engineering background to write code. Although Rust is said to have a steep learning curve as well, mostly thanks to its unique ownership model and being explicit about things that matter, we saw a big improvement in everyone’s ability to write code after switching to Rust.

We attribute this to the ergonomics and general user-friendliness of Rust – great tooling, extensive documentation and careful language design. After all, a part of the project’s mission is to empower more people to enter systems programming, a field previously dominated by C and C++ and reserved to people with deep software engineering experience.

Using Rust allows our team to write reliable, high-performance software, which is exactly what we need in flight control. On top of that, thanks to the great ergonomics, we are having a good time doing it. This is why we are such enthusiastic proponents of Rust – it has transformed our company and we are excited for more people to try it and do amazing things with it!