Design News is part of the Informa Markets Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.

3 Reasons to Use Rust in Embedded Systems

Image courtesy of Maria Vonotna / Alamy Stock Photo Rust 2F6WP29.jpg
Rust offers an exciting alternative to the traditional C/C++ programming languages. But is it right for your application?

There has always been a fierce war being waged between C/C++ for the hearts of embedded software developers. The war has lasted decades, with C++ slowly but surely whittling down the number of embedded systems built using C. C is still the most popular embedded language. Still, the debate over which language to use has intensified in recent years. What’s most interesting is that it is no longer a debate between C and C++. Instead, there is a growing trend and pressure within the software industry to use Rust. This post will explore several reasons why Rust might be a good choice for your next embedded software programming language.  

Reason #1 – Rust is a memory-safe language

Have you ever written a C/C++ application that had a memory leak? Or written an application where you destroyed an object only to learn later that the object was still in use somewhere else in the application? What about freeing the exact memory location twice or accidentally overwriting the boundaries of an array? To be frank, I’ve done all these things and worse at one point or another during my career. But, unfortunately, C and C++ were happy to let me do so and shoot myself in the foot.

The problem with the issues that I describe above is that they lead to bugs and security vulnerabilities that hackers can exploit. A memory-safe language like Rust does not allow these issues to occur. Rust will detect and tell the developer if an overflow occurs rather than silently let it happen.

For example, Figure 1 below shows a simple Rust application that initializes an array of size 5 with all zeros. The application contains a simple for loop to print out the array's values and one index beyond the array.

 

fn main() {

    println!("Hello World!");

    println!("Let's overflow the buffer!");

 

    let array: [u32; 5] = [0;5];

 

    for index in 0..array.len() + 1 {

        println!("Index {}: {} ", index, array[index])

    }

}

 

Figure 1 – A simple Rust application that attempts to read data past the array size. 

 

When I run this application using cargo run, I get the following output:

17-Rust-Figure2.png

Figure 2 – Rust catches the attempt to read past the end of the array.

 

Notice the application ran just fine, and when it came to my attempt at reading passed the buffer, it gave me a runtime error! In C/C++, the application would have been happy to read out that memory location. However, if I had been writing data, it would have been glad to overwrite and corrupt the data in that memory location.

The array example is just the tip of the iceberg. All Rust variables, by default, are immutable unless declared otherwise. There is also a concept of memory ownership. Memory safety helps developers identify critical bugs before they find their way into our production code by making us aware of them as soon as we create them!

Reason #2 – Dependency management

If you work in C/C++, you know there is no dependency manager in the languages. In fact, managing dependencies and ensuring that the correct versions of libraries are used can be a small nightmare. Some third-party dependency managers like Conan exist, but the number of teams that use these types of tools is few and far between.

Rust has a built-in package manager called Cargo that downloads package dependencies, compiles packages, distributes packages, and, if open-sourced, even upload them to crates.io. Cargo helps developers ensure that they are using the write packages and that even if new versions are available, they won’t be upgraded until they are ready to upgrade.

Cargo manages Rust applications dependencies using a toml file. The toml file for a simple application might look like the following:

 

[package]

name = "buffer_overflow"

version = "1.0.0"

edition = "2022"

 

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

 

[dependencies]

rand = "0.8.3"

 

Figure 3. A toml file for a simple application.

 

You can see in the above code snippet that the only dependency in the application is a random number generator. The version number is specified, which means if I gave you my project, I don’t need to worry about you having different versions. When you compile, Cargo will get the right packages to build the application properly.

Reason #3 – Modern and powerful

Rust compiles its code with nearly the efficiency of a C compiler. If you compare execution times, the differences are negligible. Rust can interact with low-level hardware efficiently and more safely than C/C++. There are examples of the board start-up packages where Rust can detect if a pin on the microcontroller is not configured correctly and will warn the developer. We don’t have any type of protection like that in C/C++; we just debug and poke around until we realize we didn’t initialize things properly.

The language syntax is also like what a C/C++ program might expect, with additional improvements and capabilities. For example, in C, if I want to initialize an array with 500 elements to all zeros, I need to write the following code:

 

uint32_t array[500];

 

for(uint32_t Index = 0; Index

{

    array[Index] = 0;

}

 

I’ve never found these statements to be elegant. It’s always felt weird having to initialize an array like this. In Rust, I can declare and initialize my array as follows:

 

let array: [u32; 500] = [0;500];

 

Succinct. Elegant. Concise.

 

I’ve shown you a trivial example, but there are so many examples, and the language is compelling.

 

Conclusions

Rust provides an exciting alternative to the traditional C/C++ programming languages. With so many systems looking to improve security, using a memory-safe language like Rust makes a lot of sense. However, it is still important to note that there is currently no Rust language standard. The language continues to evolve and change. Such change might not fit well with every embedded system or team. If you are interested in using Rust, I recommend you learn a bit more about it before fully committing. A few suggestions include the following:

  • Read the free Rust Embedded Book
  • Write processor start-up code for your favorite microcontroller
  • Write a serial driver
  • Develop an interrupt-based application

Only after you get some hands-on experience can you determine whether Rust fits your needs.

Hide comments
account-default-image

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish