1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-18 11:03:50 +01:00
RIOT/doc/doxygen/src/using-rust.md
Marian Buschsieweke d788bd327d
Merge pull request #17504 from chrysn-pull-requests/doc-c2rust-locked
doc: Fix C2Rust installation procedure
2022-01-17 17:40:38 +01:00

6.6 KiB

Using Rust in RIOT

[TOC]

On supported CPUs, Rust can be used to develop RIOT applications. Support is indicated in the has_rust_target feature, and tested for in applications using the Makefile line FEATURES_REQUIRED += rust_target.

In addition to the regular RIOT build toolchain and a recent nightly Rust toolchain for the given target, using this also requires C2Rust with some patches applied to be installed; see toolchain for installation instructions. All these are readily available in the official RIOT docker image, which gets used by default if BUILD_IN_DOCKER=1 is set.

Examples

Two examples are provided:

  • rust-hello-world is minimal in the sense of setup and code complexity; it is the typical Hello World example.

    (Note that it is not necessarily minimal in terms of built size, as Rust's regular printing infrastructure is more powerful and a bit heavier than your off-the-shelf printf, which embedded libcs already often trim down).

  • rust-gcoap is a set of demo CoAP resources both from the coap-message-demos crate (containing platform and library independent examples) and from the riot-module-examples crate (containing RIOT specific examples).

There are additional examples available on GitLab, maintained in coordination with the riot-wrappers crate.

How it works

The easy part of the story is that Rust code gets compiled into a static library which is then linked together with the rest of the RIOT code; if the main function happens to be implemented in Rust, so it is.

The RIOT build system contains rules and metadata to facilitate building and linking: it calls cargo (Rust's own build system), sets up paths to work well with out-of-tree builds, configures the Rust target depending on the board's CPU, and unpacks the static library into object files to facilitate interaction with XFA.

The riot-sys crate translates a selected subset of RIOT's header files for use in Rust; this happens both using the bindgen crate (working from API information in header files) and C2Rust (translating static inline functions, and with some help from riot-sys, constant preprocessor initializers). Functions exported by riot-sys are inherently unsafe to use (in Rust's sense of unsafe), and may be somewhat volatile in their API due to mismatches between RIOT's / C's and Rust's API stability concepts.

The riot-wrappers crate creates safe and idiomatic wrappers around the types and functions provided by riot-sys. Thanks to Rust's strong zero-cost abstractions, these often come at no increased runtime cost. For example, locking a riot_wrappers::mutex::Mutex can rely on it having been properly initialized at creation; furthermore, the mutex is freed when it goes out of scope.

Where practical, the wrappers are not accessed through own methods but through established platform independent traits. For example, the main API surface of an I2CDevice is its implementation of the corresponding embedded-hal I2C traits for reading and writing.

The wrapper are documented together with riot-sys and some of the examples.

Toolchain

To install the necessary Rust components, it is easiest use rustup, installed as described on its website.

Using Rust on RIOT requires a nightly version of Rust, because some transpiled expressions for RIOT make use of unstable features, and because the RIOT wrappers use some unstable idioms.

Make sure you have both the nightly toolchain and the core library for the CPU (target) of your choice available:

$ rustup toolchain add nightly
$ rustup target add thumbv7m-none-eabi --toolchain nightly

(Substitute thumbv7m-none-eavi with the value of RUST_TARGET in the output of make info-build of an application that has your current board selected, or just add it later whenever the Rust compiler complains about not finding the core library for a given target).

While Rust comes with its own cargo dependency tracker for any Rust code, it does not attempt to install system components. To avoid playing the whack-a-mole of installing components whenever an install step fails, consider installing this list of packages on Debian (or an equivalent list on the distribution of your choice):

# apt install libclang-dev llvm llvm-dev cmake libssl-dev pkg-config

This encompass both components needed for riot-sys and for the later installation of C2Rust.

Installing C2Rust is special because it can only be built using a particular nightly version (as explained in its introduction post) and needs some patches applied:

$ rustup install nightly-2019-12-05
$ rustup component add --toolchain nightly-2019-12-05 rustfmt rustc-dev
$ cargo +nightly-2019-12-05 install c2rust
$ git clone https://github.com/chrysn-pull-requests/c2rust -b for-riot
$ cd c2rust
$ cargo +nightly-2019-12-05 install --locked --path c2rust

Maintenance

The riot-sys and riot-wrappers crates are currently maintained outside of the RIOT project, primarily for practical historical reasons (all their CI has been set up on GitLab in the organization it was developed in).

As Rust is stricter in APIs than C is (for example, it is a breaking change to start returning an integer where previously void has been returned). Dealing with this, and other aspects of maintenance of the crates, is described in the rust_minimal test's README.