Why Rust is the future of robotics
Rust is a fairly recent programming language, it was first announced by Mozilla in 2010 but started by Graydon Hoare in 2006. It is behind the new, blazingly fast, Firefox Quantum web browser. It gained a lot of traction in the last few years for many very good reasons, especially for robotics.
Rust combines the advantages of low level languages such as C and C++ — mainly running really blazing fast with a really small footprint — but with a lot more guarantees that usually come from higher level languages. It prevents segmentation faults, guarantees thread safety. And where Rust particularly shines is that it enables all these safety and abstraction with no additional cost at runtime. Their motto is:
Rust: fast, reliable, productive — pick three
For these reasons, we believe Rust is the future of robotics because it enables developers to be fearless, to create and enforce standards, and to build and rely on a strong community. We explain why below, but first let us remind you what is hard in embedded software for robotics today.
Embedded programming is hard
Two main problems have proven difficult for embedded software:
- It is extremely difficult to write safe code. C and C++ use of memory is unsafe and prone to human error. Many bugs are due to buffer overflow, data corruption, or read/write in unallocated memory. Not only these are very hard to detect and debug but many attacks and virus take advantage of it. The more complex your project become, the harder it is to maintain it. Said in another way: embedded programmers fear adding extra functionalities because it could break everything!
- Concurrency is practically impossible to achieve in embedded software when going further than toy example or with massive overhead. Race conditions, deadlock, data corruption can cause bugs that are complex to debug. Especially in embedded system where the development environment is not as advanced as what you can have in the OS world. Debugging hardware interruption scares even the more courageous embedded developers.
You can find more details on why embedded software is hard here.
Beyond these technical aspects, there is also a lack of standardisation and directly reusable code, especially in robotics. Sure, there is a sample code somewhere on the internet for almost anything you want to do. But no two of them will work well together. As a result, developers today spend most of their time interfacing someone else code with their own, if not entirely recoding it. A lot of time wasted. A lot of security breaches introduced.
Rust can address all these problems, and more.
If it compiles it is safe. Let me repeat that. If it compiles it is safe. Guarantee. That is a huge pain relief for embedded programming. How Rust can manage to do that? While this goes beyond the scope of this post, let me give you some ideas.
One important principle used by Rust is ownership. Ownership tracks and ensures that each variable has only one owner at a time and, if the owner goes out of scope, the value is dropped. This principle is so powerful that it solves two important issues at once: memory safety and painless concurrency, which usually come from code accessing data when it shouldn’t.
- Memory safety — Rust does not allow null pointers or dangling pointers. There is also no garbage collector which ensure predictable code behaviour.
- Painless concurrency — With concept like borrowing, Rust can track if there is a risk of data race and simply won’t compile. It provides high-level mechanisms to wrap those conditions inside Mutex for instance and then you are good to go.
That is already something isn’t it? But at what cost? Well… pretty much zero!
- Zero-cost abstraction — Abstraction in computer science has been invented for a reason. John V. Guttag defined it nicely: ‘’The essence of abstractions is preserving information that is relevant in a given context, and forgetting information that is irrelevant in that context.’’ That is why higher level languages like Python have been invented. But Python comes with a lot of overhead processing that are hard to track, like the garbage collector, runtime check for pointer, etc. But embedded systems often handles critical applications were a worst case execution time is required. Think of digitally controlled brakes. You guessed it, Rust allows abstraction without overhead! Most checks are done at compilation so no extra computation is needed at runtime.
Rust also provides more:
- A modern syntax
- Precise error messages
- Painless packaging and dependency management — Rust comes with one of the best package manager out there, it is called Cargo. And for embedded programming, there is Xargo!
For embedded systems, specific tools have also been developed:
- Peripheral control — There is this thing called svd2rust that can automatically generate a Rust API to access every peripherals on a micro-controller, directly from it’s SVD description. Rust is smart and it can enforce at compile time that you use peripherals as they should be. You cannot compile if you try writing to a read-only register, or read to a write-only registers. You cannot write invalid bit patterns to a register too. The SVD can define a range of valid values and Rust won’t let you go out of range.
- Ressource collision prevention— In the next version, they will introduce singletons to let Rust know when some code wants to use a peripheral already in use, for example a timer. This is a common source of problems in embedded systems were ressources are limited and several devices might want to use them. It is a classic with Arduino Uno boards that only have few timers. So when you use the Servo library, you cannot use PWM on the pin 9 and 10. But the code will compile, push to the board and fail badly by producing very strange behaviours extremely hard to debug. This made many, many, Arduino users confused. With singletons, Rust can tell you at compile time if the timer is already in use, preventing users massive headaches without any runtime overhead.
- And don’t you worry, Rust is compatible with C and C++, both ways.
If you have worked with embedded programming before, you certainly pulled some hair out on those problems a few times. Rust brings confidence back, in your code, but also in the code of others you might want to reuse.
What about robotics?
There is two main points of frustration in sharing and reusing code for embedded systems:
- First, there is some category of things everyone does, like reading an analog value, logging the position of an encoder, changing the speed of a motor, or the colour of an LED. Yet, no one use the same interface and two implementations of a motor often use different function names and API to do the same things. That makes it difficult to write a generic motor controller that works for both motors.
- Second, and it comes with the first, is that we want to be confident when we reuse code of others. We want to be sure it won’t introduce new bugs because we use the code in cases not tested by the initial developers.
Most of these frustrations can be overcome naturally by Rust. From a function signature you pretty much know what the function will do and you can be sure that all variables are fully declared, initialised and used properly. If the module you add compile within your project it most likely won’t break anything (they are always some unsafe cases of course). Among many others, let me highlight two important features here:
- Traits —Familiar with object oriented programming, classes and inheritance? Rust traits are used for similar type of abstraction, but instead of Inheritance it makes use of the principle of Composition. Composition enables to define a collection of attributes that can be mixed and matched, safely. All of this at zero computational cost. If you are curious about composition, have a look at this video.
- Type Inference — In short, if a function asks for degrees and you provide radians, Rust can automatically infer that a type conversion is needed. And it will automatically use the type conversion function from radians to degrees if it has been defined. In robotics, we often need to do some math and using the wrong units often leads to bad surprises, some costing millions. Luckily Rust have your back. You can read more about type inference here.
Standards are important if we want to see robotics spreading as quickly as predicted. Rust enables sharing at scale with confidence.
Best in class community
Welcoming — A big chunk of last year focus was on lowering the learning curve for new comers, providing mentoring at all levels and improving the user experience in the edit-compile-debug cycle.
Organised —Rust have a comprehensive documentation system, it covers all the standard library and error codes. Rust community have a code of conduct and each year they summarise the community achievement and generate a new roadmap with the help of the all community. They have setup a comprehensive request for comments (RFC) process that relentlessly solicit community feedback on new developments and directions.
Reactive — We just started using Rust 4 month ago for developing library to make robotic product development simple and the dynamism of the Rust community really helped us getting up to speed quickly. Just this week, we opened a pull request on the linked-list-allocator crate, a simple allocator usable for no_std systems by Philipp Oppermann. Within 2 hours our pull request was integrated. And this happened more than once.
The community around embedded is very active and growing, with very active developers and bloggers about Embedded in Rust. Very ambitious projects are emerging, already promising a bright future with real time frameworks and secure embedded operating system. Even Android is moving to Rust.
Needless to say that an organised and disciplined community that makes you feel welcomed, listened to and supported is a strong additional drive for us to use and develop Rust in robotics.
I hope to have convinced you that Rust has tremendous potential for embedded programming and especially for robotics. Yet, the Rust embedded community is still small and many things are still missing. So have no fear, create, build, reuse standards and enjoy the community!
At first, you may feel like its a fight between you and the Rust compiler:
write some code → tries to compile → compilation fails → the way you’ve written your code makes it unsafe → insult the compiler → tries otherwise → compile again → yep it works → mumbles “ok it’s indeed better this way” :) But along the journey Rust will make you a better developper and tremendously help you write safe and efficient code!
We will keep blogging about Rust and Robots, explaining our toolchain, workflow, specific libraries, programming tips for Rust in Embedded and how we build robots. We have already open sourced all our work on github: https://github.com/pollen-robotics.
I am a software engineer/Phd, co-founder at Pollen Robotics. If you enjoyed this article, let me know by clapping or following our publication. You can subscribe to our newsletter if you want to have Rust for robotics news in your inbox!