After reading our past articles on the advantages of Rust and Rust in Embedded Systems, you have decided that you will also want to be working with this programming language. Only 1 thing is holding you back: the thought of the gigantic mountain of work you will have to get through if you want to migrate all your code to Rust…
Fortunately, we have some good news for you! One of the biggest advantages of Rust is that you do not need to migrate all your old code to this language immediately. Rust can be used perfectly well in combination with C, which means you can start with the most important/error-sensitive code and then work at your own pace until all your code has been migrated. Sounds good, right?
from c to rust.
Let’s suppose that you currently have all your code written in C, and you want to migrate to a Rust environment. How do you go about it?
Firstly and most importantly: make sure you do not rewrite all your millions of lines of code. That would be an enormous task for which no one would have the time required. Start with critical software. You can convert it to Rust and then easily integrate it with your C software.
There is of course a catch... C code is inherently ‘insecure’: you cannot therefore simply carry out 1 on 1 mapping from your C code to Rust code without making any changes as regards secure memory. And security is often precisely the main reason that developers want to migrate to Rust.
the c2rust project.
If you do however need to start translating or converting lines of C code to Rust, fortunately there are a few handy tools that can help you with this. One example is c2rust. C2rust is a project by Galois and Immunant: this tool translates C modules into semantically equivalent code in Rust. This can serve as the first step in the process of obtaining secure Rust code.
To translate C files into Rust files using c2rust, you must first run the c2rust tool with the transpile subcommand, along with the compile_commands.json file. You can create this file using cmake by executing the following command:
From there, you will receive a compile_commands.json file. Translate the C code to Rust using the command:
c2rust transpile path/to/compile_commands.json
The Rust files that you will obtain in the end will different from normal Rust modules. They export and import functions via C API. Eventually, these modules are merged in a static Rust library or binary. What is so useful here is that the functionality is retained, and test cases continue to work. The translation from generated Rust code to safe, idiomatic Rust code remains a manual step.
In addition to replacing C code with Rust code, you can also interface both languages with each other. Using the handy bindgen tool, developers can quickly and efficiently generate boilerplate code to use Rust functions in a C environment (and vice versa).
Bindgen for example makes it relatively easy to use a driver binary blob from a silicon vendor with a C interface, and interact with it from Rust code.
To be able to maintain their Rust environment, the majority of developers use the tool Cargo. Cargo is the Rust package manager, and downloads, compiles, distributes and uploads Rust packages.
As Rust's ecosystem is not as extensive as that of other languages, the language also still does not have that many libraries (or crates). Members of the Rust community add new crates every day at crates.io. Cargo and crates.io together ensure that developers can incorporate them into their project without too many problems.
In addition, there are other handy tools such as Rust Clippy, which is able to spot frequently occurring errors in your Rust code so that you will end up obtaining better Rust code.
Still feel you need some help with your migration to Rust? Feel free to contact our Rust expert Devon, so that together we can see what we can do for you.