diff --git a/doc/doxygen/src/using-rust.md b/doc/doxygen/src/using-rust.md index 50e68fd31b..563932a31d 100644 --- a/doc/doxygen/src/using-rust.md +++ b/doc/doxygen/src/using-rust.md @@ -1,258 +1,5 @@ Using Rust in RIOT {#using-rust} ================== -[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. - -[official RIOT docker image]: https://hub.docker.com/r/riot/riotbuild - -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. - -[coap-message-demos]: https://gitlab.com/chrysn/coap-message-demos -[riot-module-examples]: https://gitlab.com/etonomy/riot-module-examples -[additional examples]: https://gitlab.com/etonomy/riot-examples/ - -IDE / editor setup ------------------- - -Users of Rust often take advantage of autocompletion or inline help. -To use this on RIOT projects, -some flags and environment variables have to be set, -which are listed by `make info-rust`. -These can be configured in the IDE's project setup -or exported as environment variables. - -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 wrappers are [documented together with riot-sys and some of the examples]. - -[**riot-sys**]: https://crates.io/crates/riot-sys -[**riot-wrappers**]: https://crates.io/crates/riot-wrappers -[bindgen]: https://crates.io/crates/bindgen -[C2Rust]: https://c2rust.com/ -[riot_wrappers::mutex::Mutex]: https://rustdoc.etonomy.org/riot_wrappers/mutex/struct.Mutex.html -[documented together with riot-sys and some of the examples]: https://rustdoc.etonomy.org/ -[I2CDevice]: https://rustdoc.etonomy.org/riot_wrappers/i2c/struct.I2CDevice.html -[corresponding embedded-hal I2C traits]: https://rustdoc.etonomy.org/embedded_hal/blocking/i2c/index.html - -Library components in Rust --------------------------- - -It is possible to use Rust in different modules than the application itself. - -Such modules are usually pseudomodules (although they may be mixed with C in regular modules as well). -They always depend on the `rust_riotmodules` module / crate, -which collects all enabled modules into a single crate by means of optional features. - -If the application is not written in Rust, -that then depends on `rust_riotmodules_standalone`, -which adds a panic handler and serves as a root crate. - -If the application is written in Rust, -`rust_riotmodules` needs to be added as a dependency of the application. -(This helps deduplicate between application and library code, -and also avoids symbol name clashes). -This is done by adding a dependency on the local `rust_riotmodules` crate (which is a no-op when no such modules are enabled), -and placing an `extern crate rust_riotmodules;` statement in the code. -(The latter is needed even after most `extern crate` was abolished in 2018, -because crates depended on but not used otherwise are usually not linked in). - -Toolchain {#toolchain} ---------- - -To install the necessary Rust components, it is easiest use [**rustup**, installed as described on its website]. - -Using Rust on RIOT needs the latest stable version of Rust. - -Make sure you have the core library for the CPU (**target**) of your choice available: - -``` -$ rustup target add thumbv7m-none-eabi -``` - -Substitute thumbv7m-none-eabi 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). -Using the beta or nightly toolchains will work just as well -if they are selected through rustup's override mechanism. - - -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. - - -In addition to the Rust compiler you'll need to install the C2Rust transpiler; -as this is using some recent fixes, it is best installed as: - - -```shell -$ cargo install c2rust --git https://github.com/immunant/c2rust --tag v0.19.0 --locked -``` - -If multiple versions of LLVM are installed locally, it may be necessary to prefix it with the selected LLVM version: - -``` -$ LLVM_CONFIG_PATH=/usr/bin/llvm-config-16 cargo install … -``` - -[cargo]: https://doc.rust-lang.org/cargo/ -[**rustup**, installed as described on its website]: https://rustup.rs/ - -Maintenance ------------ - -The [riot-sys] and [riot-wrappers] crates are currently maintained as parts of the RIOT project. -While being released via crates.io on demand, usually RIOT uses a fixed version from the git repositories that are [easily updated]. - -The autogenerated bindings of the C API are slightly stricter than C's API, -and thus occasionally require additional work when C APIs change subtly. -The riot-sys crate takes the brunt of these changes -- -it changes automatically, and no attempt is currently made to reflect these changes in a SemVer compatible manner. -The riot-wrappers crate smooths this out, -and provides an API that aims to be more stable than the C API. -It does that by generously converting types that changed, -and [introspecting generated bindings] or using [information provided by riot-sys]. - -The typical workflow of (C-nonbreaking, Rust-breaking) API changes is as follows: - -* A PR subtly alters a type (eg. `uint8_t *` to `void *` in [#17990]). - - Consequently, builds of Rust examples break. - -* A PR is opened on riot-wrappers to smooth over the change, like [aab605f4] . - - The PR is tested against current master in its CI (albeit not for the full set of boards). - To test whether it also works for the changed API, - a commit titled "REMOVEME Test with updated riot-wrappes" can be added to the original PR; - it alters `.cargo/config.toml` to point to the changed branch, - and removes any Cargo.lock files in the RIOT tree. - - That PR is then merged. - -* The version of riot-wrappers that works both with the previous and the new code - is pulled into the RIOT master branch by updating the Cargo.lock files. - The PR can look like [#18181], and verifies that the new riot-wrappers works on all boards. - - That PR is then merged. - -* For the next builds (up to the merging of) the original PR, - the REMOVEME commit can be removed. - - It is good practice to rebase it onto the latest master after the update to riot-wrappers has been merged, - as this helps keeping bisectability up. - - The PR now contains no Rust specific changes, and can be merged. - -There are a few variations that can occur: - -* Sometimes casting is not enough, and a type must be extracted from a signature. - [See the phydat callback type change] for an example. - -* When neither casting nor type detection is sufficient, - a marker can be introduced through riot-sys; - it detects a keyword's presence in the source and passes it as [information provided by riot-sys] to riot-wrappers. - [See the coap_request_ctx_t change] for an example. - - In that case, a riot-sys PR is opened in parallel to the riot-wrappers PR. - - This method helps keeping changes backportable easily: - riot-sys and riot-wrappers are expected to work with the latest released version of RIOT all the time, - and avoid flag-day changes. - (Keeping riot-sys and riot-wrappers compatible with the latest release is also important to retain the ability to backport fixes). - -* When functions are moved from being static and not being static, - their names go from `riot_sys::inline::name` to `riot_sys::name` (or vice versa). - - riot-sys [has a list] of items that are always publicly exported directly as `riot_sys::name`; - just add the function there. - - If non-generic types are referenced in them, they go from `riot_sys::inline::my_type_t` to `riot_sys::my_type_t`. - The [inline_cast] function family helps making that cast a bit safer. - -* Things fail around atomics. - - Until [C2Rust's support for atomics has improved], - riot-sys requires all exported headers to use the better supported `atomic_utils.h`. - - If it is unavoidable that atomics are part of header files - (and not actually used in any static inline functions), - riot-sys's [atomics workarounds] can be extended as a last resort. - - -[riot-wrappers]: https://github.com/RIOT-OS/rust-riot-wrappers/ -[riot-sys]: https://github.com/RIOT-OS/rust-riot-sys/ -[easily updated]: https://github.com/RIOT-OS/RIOT/pull/17491#issuecomment-1143209437 -[introspecting generated bindings]: https://github.com/RIOT-OS/rust-riot-wrappers/blob/db9d163e3eddcb7154edcf25db7207e4123964ee/src/helpers.rs#L3 -[information provided by riot-sys]: https://github.com/RIOT-OS/rust-riot-sys/blob/525b2384a3541d4879a5f3845ee6241243c29a78/build.rs#L591 -[#17990]: https://github.com/RIOT-OS/RIOT/pull/17990 -[aab605f4]: https://github.com/RIOT-OS/rust-riot-wrappers/commit/aab605f464a279608ef0a8ad2afd5ae43179e330 -[#18181]: https://github.com/RIOT-OS/RIOT/pull/18181 -[See the phydat callback type change]: https://github.com/RIOT-OS/rust-riot-wrappers/pull/6/files#diff-ccb7946e3b4122ea3ce23fa9bc54eba63d75f7a6142fd4afdd9908b1bead50e0 -[See the coap_request_ctx_t change]: https://github.com/RIOT-OS/rust-riot-wrappers/pull/4/files -[has a list]: https://github.com/RIOT-OS/rust-riot-sys/blob/525b2384a3541d4879a5f3845ee6241243c29a78/build.rs#L533 -[inline_cast]: https://github.com/RIOT-OS/rust-riot-wrappers/blob/db9d163e3eddcb7154edcf25db7207e4123964ee/src/lib.rs#L68 -[C2Rust's support for atomics has improved]: https://github.com/immunant/c2rust/issues/436 -[atomics workarounds]: https://github.com/RIOT-OS/rust-riot-sys/blob/525b2384a3541d4879a5f3845ee6241243c29a78/riot-c2rust.h#L79 +@deprecated Guides have moved to the [Guide Site](https://guide.riot-os.org/). +This page will be removed after release 2025.11. diff --git a/doc/guides/rust_tutorials/create_project.mdx b/doc/guides/rust_tutorials/create_project.mdx new file mode 100644 index 0000000000..926a2d3a95 --- /dev/null +++ b/doc/guides/rust_tutorials/create_project.mdx @@ -0,0 +1,274 @@ +--- +title: "Creating a Project" +description: "Create a new project with a simple hello world program" +--- + +import GitSetup from '@components/gitsetup.mdx'; +import Contact from '@components/contact.astro'; + +## Step 1: Create a new project + + + +#### Open VS Code + +Now that we have added RIOT as a submodule to our project, +we can start writing our hello world program. +You can use any text editor to create this file. +We will use Visual Studio Code in this example. +To open Visual Studio Code in the directory, you can use the following command: + +```bash title="Open Visual Studio Code" +code . +``` + +## Step 2: Initialize Rust + +Now that Visual Studio Code is open, +we need to tell Rust what kind of project we want to create. +We do this by running the following command in the terminal: + +```bash title="Create a new Rust project" +cargo new hello_world --lib +``` + +This command creates a new project called `hello_world` with a library crate type. +The `--lib` flag tells Rust that we want to create a library crate instead of a binary crate. +A library crate is a collection of functions and types that can be used by other programs, +while a binary crate is an executable program. +RIOT then calls our `main` function when the program starts. + +You should now have 3 new files within your project directory: + +- `Cargo.toml`: This file contains metadata about your project and its dependencies. +- `src/lib.rs`: This file contains the source code for your library crate. +- `Cargo.lock`: This file contains information about the exact versions of your dependencies. + - This file is automatically generated by Cargo and should not be edited manually + so don't worry about it for now. + +![The project structure](img/create_project/01_project_structure.png) + +## Step 3: Creating the Makefile + +Now that we have created our hello world program, +we need to create a Makefile to build our program. +The Makefile is a build automation tool +that allows us to define how our program should be built. +We create a new file called `Makefile` in the root directory of our project +and add the following code: + +```makefile title="Makefile" +# name of your application +APPLICATION = hello-world + +# The name of crate (as per Cargo.toml package name, but with '-' replaced with '_') +# +# The presence of this triggers building Rust code contained in this +# application in addition to any C code. +APPLICATION_RUST_MODULE = rust_hello_world + +# If no BOARD is found in the environment, use this default: +BOARD ?= native + +# If you want to build in a Docker container, set this to 1. +# This is useful if you want to build on a system that does not have the +# RIOT toolchain installed, or if you want to ensure that the build is +# reproducible. +BUILD_IN_DOCKER ?= 1 + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../../../.. + +# Comment this out to disable code in RIOT that does safety checking +# which is not needed in a production environment but helps in the +# development process: +DEVELHELP ?= 1 + +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +include $(RIOTBASE)/Makefile.include +``` + +:::note +The `BUILD_IN_DOCKER=1` flag tells the build system to use the docker image +provided by RIOT to build our program. +This ensures that we have all the necessary dependencies to build our program. +If you have already built RIOT on your system, +you can omit this flag and the build system will use the toolchain installed on your system. +If you want to build your program on a different board, +you can change the `BOARD` variable to the name of the board you want to build for. +::: + +Now RIOT knows that you want to build a Rust application +and will use the `hello_world` crate as the main module. + +![The Makefile in Visual Studio Code](img/create_project/02_makefile.png) + +## Step 3: Adjusting the Cargo.toml + +Next, we need to adjust the `Cargo.toml` file to tell Cargo that we want to build for RIOT. +Open the `Cargo.toml` file and replace its contents with the following: + +```toml title="Cargo.toml" +[package] +name = "hello-world" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["staticlib"] + +[profile.release] +# Setting the panic mode has little effect on the built code (as Rust on RIOT +# supports no unwinding), but setting it allows builds on native without using +# the nightly-only lang_items feature. +panic = "abort" + +[dependencies] +riot-wrappers = { version = "0.9.1", features = [ "set_panic_handler", "panic_handler_format" ] } + +rust_riotmodules = { path = "./RIOT/sys/rust_riotmodules/" } +``` + +The most important part here are the dependencies. +As mentioned in [Rust in Riot](/rust_tutorials/rust_in_riot/), +`riot-wrappers` is a crate that provides a set of wrappers around +RIOT's C functions to make them usable from Rust in a safe way. + +The other configuration options are not that important for now, +but you can read more about them in the +[Cargo documentation](https://doc.rust-lang.org/cargo/reference/manifest.html). + +### IDE Setup + +Due to the way the RIOT Rust integration works, you need to amend your +normal Rust setup in your IDE to make it work with RIOT. Luckily RIOT +provides a small makefile command that will help you with that. +Run the following command in your terminal: + +```bash +make info-rust +``` + +The output will look something like this: + +```bash title="Output of make info-rust" +[ann@ann-laptop13 rust01-hello-world]$ make info-rust +cargo version +cargo 1.81.0 (2dbb1af80 2024-08-20) +c2rust --version +C2Rust 0.19.0 +To use this setup of Rust in an IDE, add these command line arguments to the `cargo check` or `rust-analyzer`: + --profile release +and export these environment variables: + CARGO_BUILD_TARGET="thumbv7em-none-eabihf" + RIOT_COMPILE_COMMANDS_JSON="/home/ann/projects/exercises/rust01-hello-world/bin/feather-nrf52840-sense/cargo-compile-commands.json" + RIOTBUILD_CONFIG_HEADER_C="/home/ann/projects/exercises/rust01-hello-world/bin/feather-nrf52840-sense/riotbuild/riotbuild.h" +You can also call cargo related commands with `make cargo-command CARGO_COMMAND="cargo check"`. +Beware that the way command line arguments are passed in is not consistent across cargo commands, so adding `--profile release` or other flags from above as part of CARGO_COMMAND may be necessary. +``` + +Add the environment variables to your shell configuration file for this project +or use the `make cargo-command` command to run cargo commands with the correct arguments. + +In VSCode you can now go to the workspace settings `.vscode/settings.json` +and add the following settings: + +```json title=".vscode/settings.json" +{ + "rust-analyzer.cargo.extraArgs": [ + "--profile", + "release" + ], + "rust-analyzer.cargo.target": "thumbv7em-none-eabihf", +} +``` + + +## Step 4: Writing the Hello World Program + +We are nearly done with setting up our project. The last thing we need to do is +to write the actual hello world program. +Open the `src/lib.rs` file and replace its contents with the following code: + +```rust title="src/lib.rs" +#![no_std] + +use riot_wrappers::riot_main; +use riot_wrappers::println; + +extern crate rust_riotmodules; +``` + +`#![no_std]` tells the Rust compiler that we are building a program +that does not depend on the standard library, +which is not available on the hardware we are targeting. +This does mean that we can't use some of the standard library features +like `std::io` or `std::collections`, however, +there are a lot of `no_std` compatible crates available that provide similar functionality, +including `riot-wrappers` 😉 + +After that we import the functions that we will soon use to print to the console. +The `riot_main` macro helps RIOT figure out what our main function is +and `println` is a macro that we can use to print to the console, +this replaces the `println!` macro from the standard library, +since, as mentioned before, we can't use it on embedded systems. + +Now we need to actually write the `main` function. +Add the following code to the `src/lib.rs` file: + +```rust title="src/lib.rs" +riot_main!(main); + +fn main() { + println!( + "You are running RIOT using Rust on a(n) {} board.", + riot_wrappers::BOARD + ); +} +``` + +![The Full Code in VSCode](img/create_project/03_lib.png) + +Congratulations! You have now created your first Rust program for RIOT. 🎉 + +## Step 5: Building the Program + + + +To build our program, we use the following command: + +```bash title="Build and flash the program" +make flash +``` + +After building the program, +we can run it using the following command to start the RIOT shell: + +```bash title="Connect to the RIOT shell" +make term +``` + +You should see the following output: + +```txt title="Output in the terminal" +You are running RIOT using Rust on a(n) native board. +``` + +![The Output in the Terminal](img/create_project/04_terminal_output.png) + +## Conclusion + +In this tutorial, +you have learned how to create a new Rust project for RIOT and how to build and run it. +You have also learned how to write a simple hello world program in Rust +and how to use the `riot-wrappers` crate to interact with RIOT's C functions. + +:::note +The source code for this tutorial can be found +[HERE](https://github.com/RIOT-OS/RIOT/tree/master/examples/lang_support/official/rust-hello-world). + +If your project is not working as expected, +you can compare your code with the code in this repository to see if you missed anything. +::: diff --git a/doc/guides/rust_tutorials/img/create_project/01_project_structure.png b/doc/guides/rust_tutorials/img/create_project/01_project_structure.png new file mode 100644 index 0000000000..a357b2fc08 Binary files /dev/null and b/doc/guides/rust_tutorials/img/create_project/01_project_structure.png differ diff --git a/doc/guides/rust_tutorials/img/create_project/02_makefile.png b/doc/guides/rust_tutorials/img/create_project/02_makefile.png new file mode 100644 index 0000000000..bf687eb7a3 Binary files /dev/null and b/doc/guides/rust_tutorials/img/create_project/02_makefile.png differ diff --git a/doc/guides/rust_tutorials/img/create_project/03_lib.png b/doc/guides/rust_tutorials/img/create_project/03_lib.png new file mode 100644 index 0000000000..f3c9c0f860 Binary files /dev/null and b/doc/guides/rust_tutorials/img/create_project/03_lib.png differ diff --git a/doc/guides/rust_tutorials/img/create_project/04_terminal_output.png b/doc/guides/rust_tutorials/img/create_project/04_terminal_output.png new file mode 100644 index 0000000000..336673ca6c Binary files /dev/null and b/doc/guides/rust_tutorials/img/create_project/04_terminal_output.png differ diff --git a/doc/guides/rust_tutorials/rust_in_riot.md b/doc/guides/rust_tutorials/rust_in_riot.md new file mode 100644 index 0000000000..1f2873eb83 --- /dev/null +++ b/doc/guides/rust_tutorials/rust_in_riot.md @@ -0,0 +1,273 @@ +--- +title: Rust in RIOT +description: Using Rust in RIOT +--- + +:::tip +This document describes the general setup and usage of Rust in RIOT. +If you want to create a new application using Rust, you can start with the +[Creating a Rust application tutorial](/rust_tutorials/create_project/). +::: + +On supported CPUs, Rust can be used to develop RIOT applications. +Support is indicated in the `has_rust_target` feature. + +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. + +[official RIOT docker image]: https://hub.docker.com/r/riot/riotbuild + +## 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 build 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. + +[coap-message-demos]: https://gitlab.com/chrysn/coap-message-demos +[riot-module-examples]: https://gitlab.com/etonomy/riot-module-examples +[additional examples]: https://gitlab.com/etonomy/riot-examples/ + +## IDE / editor setup + +Users of Rust often take advantage of autocompletion or inline help. +To use this on RIOT projects, +some flags and environment variables have to be set, +which are listed by `make info-rust`. +These can be configured in the IDE's project setup +or exported as environment variables. + +## 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 wrappers are [documented together with riot-sys and some of the examples]. + +[**riot-sys**]: https://crates.io/crates/riot-sys +[**riot-wrappers**]: https://crates.io/crates/riot-wrappers +[bindgen]: https://crates.io/crates/bindgen +[C2Rust]: https://c2rust.com/ +[riot_wrappers::mutex::Mutex]: https://rustdoc.etonomy.org/riot_wrappers/mutex/struct.Mutex.html +[documented together with riot-sys and some of the examples]: https://rustdoc.etonomy.org/ +[I2CDevice]: https://rustdoc.etonomy.org/riot_wrappers/i2c/struct.I2CDevice.html +[corresponding embedded-hal I2C traits]: https://rustdoc.etonomy.org/embedded_hal/blocking/i2c/index.html + +## Library components in Rust + +It is possible to use Rust in different modules than the application itself. + +Such modules are usually pseudomodules +(although they may be mixed with C in regular modules as well). +They always depend on the `rust_riotmodules` module / crate, +which collects all enabled modules into a single crate by means of optional features. + +If the application is not written in Rust, +that then depends on `rust_riotmodules_standalone`, +which adds a panic handler and serves as a root crate. + +If the application is written in Rust, +`rust_riotmodules` needs to be added as a dependency of the application. +(This helps deduplicate between application and library code, +and also avoids symbol name clashes). +This is done by adding a dependency on the local `rust_riotmodules` crate +(which is a no-op when no such modules are enabled), +and placing an `extern crate rust_riotmodules,` statement in the code. +(The latter is needed even after most `extern crate` was abolished in 2018, +because crates depended on but not used otherwise are usually not linked in). + +## Toolchain + +Using Rust on RIOT needs the latest stable version of Rust. Please note that +many popular Linux distributions provide very old versions in their +repositories. Therefore to install the necessary Rust components, it is +recommended and easiest to use [**rustup**, installed as described +on its project website]. + +Make sure you have the core library for the CPU (**target**) of your choice available: + +```shell +rustup target add thumbv7m-none-eabi +``` + +Substitute `thumbv7m-none-eabi` 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). +Using the beta or nightly toolchains will work just as well +if they are selected through rustup's override mechanism. + +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): + +```shell +apt install libclang-dev llvm llvm-dev cmake libssl-dev pkg-config +``` + +This encompasses both components needed for riot-sys and for the later installation of C2Rust. + +In addition to the Rust compiler you'll need to install the C2Rust transpiler, +as this is using some recent fixes, it is best installed as: + + + +```shell +cargo install c2rust --git https://github.com/immunant/c2rust --tag v0.19.0 --locked +``` + +If multiple versions of LLVM are installed locally, +it may be necessary to prefix it with the selected LLVM version: + +```shell +LLVM_CONFIG_PATH=/usr/bin/llvm-config-16 cargo install … +``` + +[cargo]: https://doc.rust-lang.org/cargo/ +[**rustup**, installed as described on its project website]: https://rustup.rs/ + +## Maintenance + +The [riot-sys] and [riot-wrappers] crates are currently maintained as parts of the RIOT project. +While being released via crates.io on demand, +usually RIOT uses a fixed version from the git repositories that are [easily updated]. + +The autogenerated bindings of the C API are slightly stricter than C's API, +and thus occasionally require additional work when C APIs change subtly. +The riot-sys crate takes the brunt of these changes -- +it changes automatically, and no attempt is currently made +to reflect these changes in a SemVer compatible manner. +The riot-wrappers crate smooths this out, +and provides an API that aims to be more stable than the C API. +It does that by generously converting types that changed, +and [introspecting generated bindings] or using [information provided by riot-sys]. + +The typical workflow of (C-nonbreaking, Rust-breaking) API changes is as follows: + +- A PR subtly alters a type (eg. `uint8_t *` to `void *` in [#17990]). + + Consequently, builds of Rust examples break. + + +- A PR is opened on riot-wrappers to smooth over the change, like [aab605f4] + + The PR is tested against current master in its CI (albeit not for the full set of boards). + To test whether it also works for the changed API, + a commit titled "REMOVEME Test with updated riot-wrappes" can be added to the original PR, + it alters `.cargo/config.toml` to point to the changed branch, + and removes any Cargo.lock files in the RIOT tree. + + That PR is then merged. + +- The version of riot-wrappers that works both with the previous and the new code + is pulled into the RIOT master branch by updating the Cargo.lock files. + The PR can look like [#18181], and verifies that the new riot-wrappers works on all boards. + + That PR is then merged. + +- For the next builds (up to the merging of) the original PR, + the REMOVEME commit can be removed. + + It is good practice to rebase it onto the latest master after + the update to riot-wrappers has been merged, + as this helps keeping bisectability up. + + The PR now contains no Rust specific changes, and can be merged. + +There are a few variations that can occur: + +- Sometimes casting is not enough, and a type must be extracted from a signature. + [See the phydat callback type change] for an example. + +- When neither casting nor type detection is sufficient, + a marker can be introduced through riot-sys, + it detects a keyword's presence in the source and passes it as + [information provided by riot-sys] to riot-wrappers. + [See the coap_request_ctx_t change] for an example. + + In that case, a riot-sys PR is opened in parallel to the riot-wrappers PR. + + This method helps keeping changes backportable easily: + riot-sys and riot-wrappers are expected to work with the latest released + version of RIOT all the time, + and avoid flag-day changes. + (Keeping riot-sys and riot-wrappers compatible with the latest release + is also important to retain the ability to backport fixes). + +- When functions are moved from being static and not being static, + their names go from `riot_sys::inline::name` to `riot_sys::name` (or vice versa). + + riot-sys [has a list] of items that are always publicly exported directly as `riot_sys::name`, + just add the function there. + + If non-generic types are referenced in them, + they go from `riot_sys::inline::my_type_t` to `riot_sys::my_type_t`. + The [inline_cast] function family helps making that cast a bit safer. + +- Things fail around atomics. + + Until [C2Rust's support for atomics has improved], + riot-sys requires all exported headers to use the better supported `atomic_utils.h`. + + If it is unavoidable that atomics are part of header files + (and not actually used in any static inline functions), + riot-sys's [atomics workarounds] can be extended as a last resort. + +[riot-wrappers]: https://github.com/RIOT-OS/rust-riot-wrappers/ +[riot-sys]: https://github.com/RIOT-OS/rust-riot-sys/ +[easily updated]: https://github.com/RIOT-OS/RIOT/pull/17491#issuecomment-1143209437 +[introspecting generated bindings]: https://github.com/RIOT-OS/rust-riot-wrappers/blob/db9d163e3eddcb7154edcf25db7207e4123964ee/src/helpers.rs#L3 +[information provided by riot-sys]: https://github.com/RIOT-OS/rust-riot-sys/blob/525b2384a3541d4879a5f3845ee6241243c29a78/build.rs#L591 +[#17990]: https://github.com/RIOT-OS/RIOT/pull/17990 +[aab605f4]: https://github.com/RIOT-OS/rust-riot-wrappers/commit/aab605f464a279608ef0a8ad2afd5ae43179e330 +[#18181]: https://github.com/RIOT-OS/RIOT/pull/18181 +[See the phydat callback type change]: https://github.com/RIOT-OS/rust-riot-wrappers/pull/6/files#diff-ccb7946e3b4122ea3ce23fa9bc54eba63d75f7a6142fd4afdd9908b1bead50e0 +[See the coap_request_ctx_t change]: https://github.com/RIOT-OS/rust-riot-wrappers/pull/4/files +[has a list]: https://github.com/RIOT-OS/rust-riot-sys/blob/525b2384a3541d4879a5f3845ee6241243c29a78/build.rs#L533 +[inline_cast]: https://github.com/RIOT-OS/rust-riot-wrappers/blob/db9d163e3eddcb7154edcf25db7207e4123964ee/src/lib.rs#L68 +[C2Rust's support for atomics has improved]: https://github.com/immunant/c2rust/issues/436 +[atomics workarounds]: https://github.com/RIOT-OS/rust-riot-sys/blob/525b2384a3541d4879a5f3845ee6241243c29a78/riot-c2rust.h#L79 diff --git a/doc/starlight/astro.config.mjs b/doc/starlight/astro.config.mjs index bac0e62d36..914a5f4d6b 100644 --- a/doc/starlight/astro.config.mjs +++ b/doc/starlight/astro.config.mjs @@ -73,6 +73,13 @@ export default defineConfig({ "c_tutorials/saul", ], }, + { + label: "Rust Basics", + items: [ + "rust_tutorials/rust_in_riot", + "rust_tutorials/create_project", + ], + }, ], }, {