## Introduction
Various parties have expressed interest in having "Rust Suppo…rt" in Zephyr. This can mean different things, such as writing applications in Rust, to writing Zephyr drivers in Rust. This RFC attempts to define a specific type of support for Rust within Zephyr.
### Problem description
The [Rust programming language](https://rust-lang.org/) is a relatively modern programming language that attempts to bring together several features that would be appropriate for the type of embedded development Zephyr is commonly used for. Among these are:
- Strong typing. The rust typing system allows many common programming and especially security issues to be found at compile time.
- A sophisticated borrow tracking system. The borrow system in Rust allows compile time tracking of references, such that, in many programs, it can be known at compile time that there are never any invalid references, dangling pointers, out of bounds accesses, etc. This can even be managed safely while using dynamic memory allocation, if that is desired.
- "Fearless concurrency". This same borrow system can also ensure that values used in multi-threaded applications are managed safely, resulting in systems where invalid access by multiple threads is caught at compile time.
- Zero cost abstractions. The language brings the ability to have rich abstractions that add no cost to the generated code.
This RFC proposes a way for an application developer to be able to write their application, in Rust, using idiomatic Rust code (meaning `unsafe` is not generally needed) on top of Zephyr, while maintaining a minimal overhead to do so.
### Proposed change
This support will be added in a few places:
- Additional modules: There will be some additional modules that provide Rust crates. These can be included using Zephyr-type methods (where they are directly brought into the tree using west). These could also be published to the general crate ecosystem, if any particular crates prove useful to the general Rust community. Primary of these crates will be a `zephyr` crate which will be the main crate an application uses.
- CMake support: There will be additional CMake support so that an application merely needs to reference a CMake library in order to declare they are building a Rust application. This support will invoke the `cargo` command to run the Rust compile, with proper arguments for the chosen board as well as necessary paths to integrate the Rust build into the Zephyr build system.
- DeviceTree support: In addition to the C headers that are currently generated, the dt scripts will be extended to be able to generate Rust code to represent the current device tree. This can be implemented gradually as more and more parts of the Zephyr api and device surface is exposed to Rust. Most of the implementation of this interface will be done in the additional modules (crates) above, and this generated code is merely describing the particular device layout for a given build.
- Zephyr system call interface. Those calls into Zephyr that are system calls or function calls, depending on the configuration will have a generated Rust crate/module that implements them for the current configuration.
## Detailed RFC
### Overall Goal.
When completed, it should be possible to have a rust implementation of "blinky" that looks something like:
```rust
#![no_std]
#![no_main]
#[zephyr::entry]
fn main() {
let dt = zephyr::devicetree::take().unwrap();
let led = dt.chosen.led0;
loop {
led.toggle().unwrap();
zephyr::sys::sleep(1000);
}
}
```
This can be filled out as the Rust interface to Zephyr features are implemented. But, this level of initial support is probably a good starting point.
### Devicetree
Currently, the device tree is effectively flattened into a set of C defines for all of the values. Although it would be possible for Rust code to access values like this, the macro name stitching that is done in C would be both rather non-rust-like, as well as hide the entire device tree from tools like IDEs. It would make the generated tree mostly undiscoverable to a developer trying to write code.
Instead, we propose generating a fairly simple structure in Rust that mirrors the device tree. This structure is modeled after the PAC/HAL structures that are used within the Zephyr Embedded project. Ultimately, there is a single struct, the DeviceTree that is generated. This could, perhaps initially, be modeled after the chosen node, with only supported nodes included. Each field of this struct would be an implementation of a structure defined in a support crate for that particular device. The end result would be similar to the structures that are generated through macros to use device tree nodes currently within C.
### Driver support
To make this work, much of the support Rust code will be human written code that defines the types, and methods upon those types that implement the interface to the Zephyr drivers. For some drivers, this is fairly straightforward, and will mainly result in using a cleaner Rust namespace to invoke the Zephyr devices. Support for drivers that are implemented in C with callbacks is a more complicated issue and address further below.
### CMake Support
At a minimum, the cmake files will need to be extended to support building a Rust application. It will need to invoke the additional device tree generation code, convert Kconfig options to rust features, as appropriate (details to be filled in), and provide a small cmake library so that it is easy for an application to state that it is a Rust application. The cmake code will create rules that invoke `cargo` with the appropriate arguments, and arrange for the compiled application library to be linked into the Zephyr application.
### System call support
The kernel libraries in Zephyr will be made available through a `zephyr::sys` module that will be a fairly straightforward mapping from Rust to the C functions/syscalls. For some routines, such as sleep, it makes sense to just call these from ordinary Rust applications. For others, such as threading, it probably makes sense to have higher level abstractions built around this.
### Asynchronous support
The general trend in the Rust embedded world is to use the `async` mechanism to write code that is multi-threaded and needs to block. This proposal will implement a basic executor using the Zephyr scheduling primitives. A good mechanism can then be used to associate simple callbacks in driver calls to functions that send to semaphores, for example, and the semaphore and other scheduling primitives will have support in the async code to be able to wait for them.
It would also be possible to just have a 1:1 mapping of Rust threads to zephyr threads, and the primitive calls would just block the current thread to be woken appropriately. Ideally, either of these mechanisms will be available to Rust programmers to use as appropriate for a given application.
### Logging
The Rust and Zephyr logging systems have some fairly significant semantic mismatches. Fortunately, The Rust language does not explicitly define a logging mechanism, and it would be fairly easy to have some simple wrappers. Log 2 would likely need to be used, as the formatting mechanism are dissimilar between the languages, and the Rust logged messages would likely just be formatted strings.
It should also be possible to configure the system to not have the Zephyr logging mechanism, and use something like the Rust `defmt` logger, which greatly compresses the log stream by referencing strings by index and resolving the strings by a host tool.
### Dependencies
The main concern is to make sure that any support added to Zephyr for Rust should be tested as part of CI. The primary reason is so that API and other changes that break this support will be caught early, and can be fixed.
### Concerns and Unresolved Questions
One concern would be how "official" rust support is by the Zephyr project. In some sense, this is largely outside of the Zephyr tree (in modules), but as it depends on the Zephyr API, if not well supported could quickly lock a given application to a specific version of Zephyr.
## Alternatives
Obviously, it is possible to continue to write code in C, or C++. However, many users and companies are finding a lot of advantages to developing in Rust, and it should be possible to add this support to Zephyr without significant impact on how C development is done.
Any efforts or desires to implement parts of Zephyr in Rust are beyond the scope of this RFC, and this kind of support would have a much larger impact.