|
1 | 1 | Using Rust in RIOT {#using-rust} |
2 | 2 | ================== |
3 | 3 |
|
4 | | -[TOC] |
5 | | - |
6 | | -On supported CPUs, Rust can be used to develop RIOT applications. |
7 | | -Support is indicated in the `has_rust_target` feature, |
8 | | -and tested for in applications using the Makefile line |
9 | | -`FEATURES_REQUIRED += rust_target`. |
10 | | - |
11 | | -In addition to the regular RIOT build toolchain |
12 | | -and a recent nightly Rust toolchain for the given target, |
13 | | -using this also requires C2Rust with some patches applied to be installed; |
14 | | -see <a href="#toolchain">toolchain</a> for installation instructions. |
15 | | -All these are readily available in the [official RIOT docker image], |
16 | | -which gets used by default if `BUILD_IN_DOCKER=1` is set. |
17 | | - |
18 | | -[official RIOT docker image]: https://hub.docker.com/r/riot/riotbuild |
19 | | - |
20 | | -Examples |
21 | | --------- |
22 | | - |
23 | | -Two examples are provided: |
24 | | - |
25 | | -* ``rust-hello-world`` is minimal in the sense of setup and code complexity; it is the typical Hello World example. |
26 | | - |
27 | | - (Note that it is not necessarily minimal in terms of built size, |
28 | | - as Rust's regular printing infrastructure is more powerful and a bit heavier than your off-the-shelf ``printf``, |
29 | | - which embedded libcs already often trim down). |
30 | | - |
31 | | -* ``rust-gcoap`` is a set of demo CoAP resources |
32 | | - both from the [coap-message-demos] crate (containing platform and library independent examples) |
33 | | - and from the [riot-module-examples] crate (containing RIOT specific examples). |
34 | | - |
35 | | -There are [additional examples] available on GitLab, |
36 | | -maintained in coordination with the riot-wrappers crate. |
37 | | - |
38 | | -[coap-message-demos]: https://gitlab.com/chrysn/coap-message-demos |
39 | | -[riot-module-examples]: https://gitlab.com/etonomy/riot-module-examples |
40 | | -[additional examples]: https://gitlab.com/etonomy/riot-examples/ |
41 | | - |
42 | | -IDE / editor setup |
43 | | ------------------- |
44 | | - |
45 | | -Users of Rust often take advantage of autocompletion or inline help. |
46 | | -To use this on RIOT projects, |
47 | | -some flags and environment variables have to be set, |
48 | | -which are listed by `make info-rust`. |
49 | | -These can be configured in the IDE's project setup |
50 | | -or exported as environment variables. |
51 | | - |
52 | | -How it works |
53 | | ------------- |
54 | | - |
55 | | -The easy part of the story is that Rust code gets compiled into a static library |
56 | | -which is then linked together with the rest of the RIOT code; |
57 | | -if the main function happens to be implemented in Rust, so it is. |
58 | | - |
59 | | -The **RIOT build system** contains rules and metadata to facilitate building and linking: |
60 | | -it calls `cargo` (Rust's own build system), |
61 | | -sets up paths to work well with out-of-tree builds, |
62 | | -configures the Rust target depending on the board's CPU, |
63 | | -and unpacks the static library into object files to facilitate interaction with XFA. |
64 | | - |
65 | | -The [**riot-sys**] crate translates a selected subset of RIOT's header files for use in Rust; |
66 | | -this happens both using the [bindgen] crate (working from API information in header files) |
67 | | -and [C2Rust] \(translating static inline functions, and with some help from riot-sys, constant preprocessor initializers). |
68 | | -Functions exported by riot-sys are inherently unsafe to use (in Rust's sense of unsafe), |
69 | | -and may be somewhat volatile in their API due to mismatches between RIOT's / C's and Rust's API stability concepts. |
70 | | - |
71 | | -The [**riot-wrappers**] crate creates safe and idiomatic wrappers around the types and functions provided by riot-sys. |
72 | | -Thanks to Rust's strong zero-cost abstractions, these often come at no increased runtime cost. |
73 | | -For example, locking a [riot_wrappers::mutex::Mutex] can rely on it having been properly initialized at creation; |
74 | | -furthermore, the mutex is freed when it goes out of scope. |
75 | | - |
76 | | -Where practical, the wrappers are not accessed through own methods |
77 | | -but through established platform independent traits. |
78 | | -For example, the main API surface of an [I2CDevice] |
79 | | -is its implementation of the [corresponding embedded-hal I2C traits] for reading and writing. |
80 | | - |
81 | | -The wrappers are [documented together with riot-sys and some of the examples]. |
82 | | - |
83 | | -[**riot-sys**]: https://crates.io/crates/riot-sys |
84 | | -[**riot-wrappers**]: https://crates.io/crates/riot-wrappers |
85 | | -[bindgen]: https://crates.io/crates/bindgen |
86 | | -[C2Rust]: https://c2rust.com/ |
87 | | -[riot_wrappers::mutex::Mutex]: https://rustdoc.etonomy.org/riot_wrappers/mutex/struct.Mutex.html |
88 | | -[documented together with riot-sys and some of the examples]: https://rustdoc.etonomy.org/ |
89 | | -[I2CDevice]: https://rustdoc.etonomy.org/riot_wrappers/i2c/struct.I2CDevice.html |
90 | | -[corresponding embedded-hal I2C traits]: https://rustdoc.etonomy.org/embedded_hal/blocking/i2c/index.html |
91 | | - |
92 | | -Library components in Rust |
93 | | --------------------------- |
94 | | - |
95 | | -It is possible to use Rust in different modules than the application itself. |
96 | | - |
97 | | -Such modules are usually pseudomodules (although they may be mixed with C in regular modules as well). |
98 | | -They always depend on the `rust_riotmodules` module / crate, |
99 | | -which collects all enabled modules into a single crate by means of optional features. |
100 | | - |
101 | | -If the application is not written in Rust, |
102 | | -that then depends on `rust_riotmodules_standalone`, |
103 | | -which adds a panic handler and serves as a root crate. |
104 | | - |
105 | | -If the application is written in Rust, |
106 | | -`rust_riotmodules` needs to be added as a dependency of the application. |
107 | | -(This helps deduplicate between application and library code, |
108 | | -and also avoids symbol name clashes). |
109 | | -This is done by adding a dependency on the local `rust_riotmodules` crate (which is a no-op when no such modules are enabled), |
110 | | -and placing an `extern crate rust_riotmodules;` statement in the code. |
111 | | -(The latter is needed even after most `extern crate` was abolished in 2018, |
112 | | -because crates depended on but not used otherwise are usually not linked in). |
113 | | - |
114 | | -Toolchain {#toolchain} |
115 | | ---------- |
116 | | - |
117 | | -To install the necessary Rust components, it is easiest use [**rustup**, installed as described on its website]. |
118 | | - |
119 | | -Using Rust on RIOT needs the latest stable version of Rust. |
120 | | - |
121 | | -Make sure you have the core library for the CPU (**target**) of your choice available: |
122 | | - |
123 | | -``` |
124 | | -$ rustup target add thumbv7m-none-eabi |
125 | | -``` |
126 | | - |
127 | | -Substitute thumbv7m-none-eabi with the value of `RUST_TARGET` |
128 | | -in the output of `make info-build` of an application that has your current board selected |
129 | | -(or just add it later whenever the Rust compiler complains about not finding the core library for a given target). |
130 | | -Using the beta or nightly toolchains will work just as well |
131 | | -if they are selected through rustup's override mechanism. |
132 | | - |
133 | | - |
134 | | -While Rust comes with its own [cargo] dependency tracker for any Rust code, |
135 | | -it does not attempt to install **system components**. |
136 | | -To avoid playing the whack-a-mole of installing components whenever an install step fails, |
137 | | -consider installing this list of packages on Debian |
138 | | -(or an equivalent list on the distribution of your choice): |
139 | | - |
140 | | -``` |
141 | | -# apt install libclang-dev llvm llvm-dev cmake libssl-dev pkg-config |
142 | | -``` |
143 | | - |
144 | | -This encompass both components needed for riot-sys and for the later installation of C2Rust. |
145 | | - |
146 | | - |
147 | | -In addition to the Rust compiler you'll need to install the C2Rust transpiler; |
148 | | -as this is using some recent fixes, it is best installed as: |
149 | | - |
150 | | -<!-- The locked works around <https://github.com/dtolnay/proc-macro2/issues/475> as closed in <https://github.com/immunant/c2rust/pull/1197>; there is no newer release that could be tested. --> |
151 | | -```shell |
152 | | -$ cargo install c2rust --git https://github.com/immunant/c2rust --tag v0.19.0 --locked |
153 | | -``` |
154 | | - |
155 | | -If multiple versions of LLVM are installed locally, it may be necessary to prefix it with the selected LLVM version: |
156 | | - |
157 | | -``` |
158 | | -$ LLVM_CONFIG_PATH=/usr/bin/llvm-config-16 cargo install … |
159 | | -``` |
160 | | - |
161 | | -[cargo]: https://doc.rust-lang.org/cargo/ |
162 | | -[**rustup**, installed as described on its website]: https://rustup.rs/ |
163 | | - |
164 | | -Maintenance |
165 | | ------------ |
166 | | - |
167 | | -The [riot-sys] and [riot-wrappers] crates are currently maintained as parts of the RIOT project. |
168 | | -While being released via crates.io on demand, usually RIOT uses a fixed version from the git repositories that are [easily updated]. |
169 | | - |
170 | | -The autogenerated bindings of the C API are slightly stricter than C's API, |
171 | | -and thus occasionally require additional work when C APIs change subtly. |
172 | | -The riot-sys crate takes the brunt of these changes -- |
173 | | -it changes automatically, and no attempt is currently made to reflect these changes in a SemVer compatible manner. |
174 | | -The riot-wrappers crate smooths this out, |
175 | | -and provides an API that aims to be more stable than the C API. |
176 | | -It does that by generously converting types that changed, |
177 | | -and [introspecting generated bindings] or using [information provided by riot-sys]. |
178 | | - |
179 | | -The typical workflow of (C-nonbreaking, Rust-breaking) API changes is as follows: |
180 | | - |
181 | | -* A PR subtly alters a type (eg. `uint8_t *` to `void *` in [#17990]). |
182 | | - |
183 | | - Consequently, builds of Rust examples break. |
184 | | - |
185 | | -* A PR is opened on riot-wrappers to smooth over the change, like [aab605f4] <!-- commit reference rather than PR as that was still on GitLab back then -->. |
186 | | - |
187 | | - The PR is tested against current master in its CI (albeit not for the full set of boards). |
188 | | - To test whether it also works for the changed API, |
189 | | - a commit titled "REMOVEME Test with updated riot-wrappes" can be added to the original PR; |
190 | | - it alters `.cargo/config.toml` to point to the changed branch, |
191 | | - and removes any Cargo.lock files in the RIOT tree. |
192 | | - |
193 | | - That PR is then merged. |
194 | | - |
195 | | -* The version of riot-wrappers that works both with the previous and the new code |
196 | | - is pulled into the RIOT master branch by updating the Cargo.lock files. |
197 | | - The PR can look like [#18181], and verifies that the new riot-wrappers works on all boards. |
198 | | - |
199 | | - That PR is then merged. |
200 | | - |
201 | | -* For the next builds (up to the merging of) the original PR, |
202 | | - the REMOVEME commit can be removed. |
203 | | - |
204 | | - It is good practice to rebase it onto the latest master after the update to riot-wrappers has been merged, |
205 | | - as this helps keeping bisectability up. |
206 | | - |
207 | | - The PR now contains no Rust specific changes, and can be merged. |
208 | | - |
209 | | -There are a few variations that can occur: |
210 | | - |
211 | | -* Sometimes casting is not enough, and a type must be extracted from a signature. |
212 | | - [See the phydat callback type change] for an example. |
213 | | - |
214 | | -* When neither casting nor type detection is sufficient, |
215 | | - a marker can be introduced through riot-sys; |
216 | | - it detects a keyword's presence in the source and passes it as [information provided by riot-sys] to riot-wrappers. |
217 | | - [See the coap_request_ctx_t change] for an example. |
218 | | - |
219 | | - In that case, a riot-sys PR is opened in parallel to the riot-wrappers PR. |
220 | | - |
221 | | - This method helps keeping changes backportable easily: |
222 | | - riot-sys and riot-wrappers are expected to work with the latest released version of RIOT all the time, |
223 | | - and avoid flag-day changes. |
224 | | - (Keeping riot-sys and riot-wrappers compatible with the latest release is also important to retain the ability to backport fixes). |
225 | | - |
226 | | -* When functions are moved from being static and not being static, |
227 | | - their names go from `riot_sys::inline::name` to `riot_sys::name` (or vice versa). |
228 | | - |
229 | | - riot-sys [has a list] of items that are always publicly exported directly as `riot_sys::name`; |
230 | | - just add the function there. |
231 | | - |
232 | | - If non-generic types are referenced in them, they go from `riot_sys::inline::my_type_t` to `riot_sys::my_type_t`. |
233 | | - The [inline_cast] function family helps making that cast a bit safer. |
234 | | - |
235 | | -* Things fail around atomics. |
236 | | - |
237 | | - Until [C2Rust's support for atomics has improved], |
238 | | - riot-sys requires all exported headers to use the better supported `atomic_utils.h`. |
239 | | - |
240 | | - If it is unavoidable that atomics are part of header files |
241 | | - (and not actually used in any static inline functions), |
242 | | - riot-sys's [atomics workarounds] can be extended as a last resort. |
243 | | - |
244 | | - |
245 | | -[riot-wrappers]: https://github.com/RIOT-OS/rust-riot-wrappers/ |
246 | | -[riot-sys]: https://github.com/RIOT-OS/rust-riot-sys/ |
247 | | -[easily updated]: https://github.com/RIOT-OS/RIOT/pull/17491#issuecomment-1143209437 |
248 | | -[introspecting generated bindings]: https://github.com/RIOT-OS/rust-riot-wrappers/blob/db9d163e3eddcb7154edcf25db7207e4123964ee/src/helpers.rs#L3 |
249 | | -[information provided by riot-sys]: https://github.com/RIOT-OS/rust-riot-sys/blob/525b2384a3541d4879a5f3845ee6241243c29a78/build.rs#L591 |
250 | | -[#17990]: https://github.com/RIOT-OS/RIOT/pull/17990 |
251 | | -[aab605f4]: https://github.com/RIOT-OS/rust-riot-wrappers/commit/aab605f464a279608ef0a8ad2afd5ae43179e330 |
252 | | -[#18181]: https://github.com/RIOT-OS/RIOT/pull/18181 |
253 | | -[See the phydat callback type change]: https://github.com/RIOT-OS/rust-riot-wrappers/pull/6/files#diff-ccb7946e3b4122ea3ce23fa9bc54eba63d75f7a6142fd4afdd9908b1bead50e0 |
254 | | -[See the coap_request_ctx_t change]: https://github.com/RIOT-OS/rust-riot-wrappers/pull/4/files |
255 | | -[has a list]: https://github.com/RIOT-OS/rust-riot-sys/blob/525b2384a3541d4879a5f3845ee6241243c29a78/build.rs#L533 |
256 | | -[inline_cast]: https://github.com/RIOT-OS/rust-riot-wrappers/blob/db9d163e3eddcb7154edcf25db7207e4123964ee/src/lib.rs#L68 |
257 | | -[C2Rust's support for atomics has improved]: https://github.com/immunant/c2rust/issues/436 |
258 | | -[atomics workarounds]: https://github.com/RIOT-OS/rust-riot-sys/blob/525b2384a3541d4879a5f3845ee6241243c29a78/riot-c2rust.h#L79 |
| 4 | +@deprecated Guides have moved to the [Guide Site](https://guide.riot-os.org/). |
| 5 | +This page will be removed after release 2025.11. |
0 commit comments