Rust ❤️ Prodrive
About me
- Daan de Graaf
- Software Science @ TU/e
- Designer @ Prodrive
- Writing Rust since 2015


What this isn't
- A tutorial on Rust programming
- A rant on C, C++ and other languages
- Rust evangelism
What this is
- Practical experiences here at Prodrive
- A highlight of the advantages of Rust
- When to use Rust

About Rust
- Created by Mozilla
- A faster, safer browser
- No garbage collection
- Strong type system
- C-style syntax
Empowering everyone to build
reliable and efficient software.
Most loved programming language
(Stack Overflow 2016-2019)
Why use Rust?
- Performance
- Reliability
- Productivity

Performance
Comparable to C and C++:
- No garbage collection
- Highly optimized compiler
- Zero cost abstractions
Rust
C
C++
Asm
Java
Go
C#
Memory safe
High-level
High performance
Low-level
Rust + LLVM

Clang
IR
Machine code
Case in point
pub fn square(num: i32) -> i32 {
num * num
}int square(int num) {
return num * num;
}square:
imul edi, edi
mov eax, edi
retZero cost abstractions
#[derive(Clone)]
struct System {
name: String,
active: bool,
// More stuff ...
}
struct DataStore {
systems: Vec<System>,
// More stuff ...
}
fn active_systems(data: &DataStore) -> Vec<Json<System>> {
data.systems.iter() // Create a "stream" of items
.filter(|s| s.active) // Only the ones that are active
.take(5) // No more than five items
.map(|s| Json(s.clone())) // Turn it into JSON
.collect() // Store the results in a list
// `return` keyword is implicit
}Still equivalent
pub fn any_true(list: &[bool]) -> bool {
list.iter().any(|x| *x == true)
}bool any_true(bool *list, size_t len) {
for(size_t i = 0; i < len; i++) {
if(list[i]) return true;
}
return false;
}any_true:
test rsi, rsi
je .L7
movzx eax, BYTE PTR [rdi]
test al, al
jne .L3
lea rdx, [rdi+1]
add rsi, rdi
jmp .L5
.L6:
add rdx, 1
movzx ecx, BYTE PTR [rdx-1]
test cl, cl
jne .L8
.L5:
cmp rdx, rsi
jne .L6
ret
.L8:
mov eax, ecx
.L3:
ret
.L7:
xor eax, eax
retany_true:
test rsi, rsi
je .L7
movzx eax, BYTE PTR [rdi]
test al, al
jne .L3
lea rdx, [rdi+1]
add rsi, rdi
jmp .L5
.L6:
add rdx, 1
movzx ecx, BYTE PTR [rdx-1]
test cl, cl
jne .L8
.L5:
cmp rdx, rsi
jne .L6
ret
.L8:
mov eax, ecx
.L3:
ret
.L7:
xor eax, eax
retQuiz time!
#include <vector>
#include <iostream>
int main() {
std::vector<int> list;
list.push_back(1);
auto item1 = &list[0];
std::cout << "Item: " << (*item1) << std::endl;
}#include <vector>
#include <iostream>
int main() {
std::vector<int> list;
list.push_back(1);
auto item1 = &list[0];
list.push_back(2);
auto item2 = &list[1];
std::cout << "Items: " << (*item1) << ", " << (*item2) << std::endl;
}fn main() {
let mut list = Vec::new();
list.push(1);
let item1 = &list[0];
list.push(2);
let item2 = &list[1];
println!("Items are: {}, {}", item1, item2);
}error[E0502]: cannot borrow `list` as mutable because it is also borrowed as immutable
--> src/main.rs:5:5
|
4 | let item1 = &list[0];
| ---- immutable borrow occurs here
5 | list.push(2);
| ^^^^^^^^^^^^ mutable borrow occurs here
6 | let item2 = &list[1];
7 | println!("Items are: {}, {}", item1, item2);
| ----- immutable borrow later used here
Items are: 0, 2Impossible in Rust:
- Null pointers
- Use after free
- Double free
- Buffer overflows
If it compiles, it runs

Explicit errors
#include <stdio.h>
const char DATA[] = { 0x72, 0x75, 0x73, 0x74};
int main() {
FILE* fd = fopen("hello_world.txt", "w");
fwrite(DATA, sizeof(char), sizeof(DATA) / sizeof(char), fd);
}#include <stdio.h>
const char DATA[] = { 0x72, 0x75, 0x73, 0x74};
int main() {
FILE* fd = fopen("hello_world.txt", "w");
if(fd == NULL) {
perror("Could not open file");
return 1;
}
size_t len = fwrite(DATA, sizeof(char), sizeof(DATA) / sizeof(char), fd);
if(len != sizeof(DATA) / sizeof(char)) {
perror("Could not write to file");
return 1;
}
if(fclose(fd)) {
perror("Could not close file");
return 1;
}
return 0;
}use std::fs::File;
use std::io::{Write, Error};
// We return a Result, which could be one of:
// * Ok Everything went well.
// * Err There was an error (which will be printed to stdout).
fn main() -> Result<(), Error> {
// Create a new file
let mut file = File::create("hello_world.txt")?;
file.write(&[0x72, 0x75, 0x73, 0x74])?;
// Report that everything went okay
Ok(())
// File is automatically closed here
}use std::fs::File;
use std::io::{Write, Error};
fn main() {
// Create a new file
let mut file = File::create("hello_world.txt");
file.write(&[0x72, 0x75, 0x73, 0x74]);
}
error[E0599]: no method named `write` found for type `Result<File, Error>`
--> src/main.rs:7:10
|
7 | file.write(&[0x72, 0x75, 0x73, 0x74]);
| Cargo
Rust's awesome package manager
Build any Rust project:
[daagra@prodrive rust_project]$ cargo buildNeed a web server?
[package]
name = "rust_project"
version = "0.1.0"
[dependencies]
actix-web = "0.7"Show me the docs!
[daagra@prodrive rust_project]$ cargo doc --open
case I: redfish_proxyd
- Web server for remote cluster management
- REST API server written in C
- Concurrency bottlenecks
- Low developer velocity
- Memory leaks
- Proof of concept in Rust

Thomas Sioutas
Concurrency
redfish_proxyd
redfishd
redfishd
redfishd
...
Poll
Options in C:
- Stay sequential
- pthreads, tricky synchronisation
Options in Rust:
- Native safe threads
- Futures
Back to the future
HTTP GET System
Parse JSON
HTTP GET Chassis
Parse JSON
Combine to Server struct
Encode JSON
Response
JSON serialization
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
struct EthernetInterface {
host_name: String,
#[serde(rename = "FQDN")]
fqdn: String,
full_duplex: Option<bool>,
interface_enabled: Option<bool>,
ipv4_addresses: Vec<Ipv4Addr>,
}{
"HostName": "localhost",
"FQDN": "localhost.lan",
"InterfaceEnabled": false,
"Ipv4Addresses": [
"192.168.1.1"
]
}EthernetInterface {
host_name: "localhost",
fqdn: "localhost.lan",
full_duplex: None,
interface_enabled: Some(true),
ipv4_addresses: [
Ipv4Addr(192, 168, 1, 1),
]
}Architecture
main()
redfishd poller
files, network, ...
1. Create/start
Router
- Bind server
- Route requests
2. Create/start
Injected
Outcome I: Adoption
Rust replaces original implementation


Krzystof++
Case II: IOT_STM32
- Prodrive Framework for Internet of Things
- Close to bare metal (FreeRTOS)
- C - Rust - C sandwich
Rust on embedded
- Architectures
- ARM Cortex-M, Cortex-A
- RISC-V
- MIPS
- Board support crates
- Embedded HAL
Embedded HAL
PWM
I2C
SPI
Timers
Watchdog
Board support crates
Driver crates

Implements
Depends on
C interop
- Vital use-case for Rust
- Zero overhead
- Tooling: Binding generation in both directions

C- Rust - C sandwich
// Implemented in Rust
extern void sum_array(int *arr, size_t len, void (*cb)(int sum));
void callback(int sum) {
printf("Sum: %d\n", sum);
}
int main() {
int numbers[] = {1, 2, 3, 4};
sum_array(numbers, 4, callback);
}use core::slice;
type Callback = extern "C" fn(sum: i32);
// no_mangle attribute to stick to a C style name in the object file.
#[no_mangle]
pub extern "C" fn sum_array(arr: *const i32, len: usize, cb: Callback) {
// Convert from an unsafe C array to a safe Rust version
let array = unsafe { slice::from_raw_parts(arr, len) };
let sum = array.iter().sum();
cb(sum);
}CBOR parsing
- Complex and risky operation
- Input supplied by third party
- Large data volume
- Scattered on different files
- Leverage serialization framework
- Needs bindings to PTFS
CBOR parsing: result
use crate::parse::ParseError;
use crate::ptfs::File;
use cstr_core::CStr;
#[derive(Debug, Deserialize, Default)]
pub struct Operator {
#[serde(rename = "oID")]
o_id: u32,
#[serde(rename = "fID")]
f_id: u16,
#[serde(rename = "aLvl")]
a_lvl: u8,
#[serde(rename = "exp")]
exp_datetime: u32,
#[serde(rename = "pMode")]
p_mode: u8,
}
pub fn parse_operator(path: &CStr) -> Result<Operator, ParseError> {
let file = File::open(path)?;
// Reserve space to store our parsed operator
let mut operator = Operator::default();
// Buffer for temporary parse data
let mut buffer = [0u8; 64];
file.deserialize_into(&mut buffer, &mut operator)?;
Ok(operator)
}
Outcome II: Exploration
Further experiments in progress

Patrick
Bottom line
Rust promises great things ...
and delivers!

When not to use Rust
- Low performance and/or safety requirements
- Large libraries without good bindings
- OpenCV
- Exotic platforms without LLVM support
- xtensa (ESP32), for now
- armv5
- avr

When should I consider Rust?
- You need C-like performance
- Safety and stability are important
- You need concurrency
- Precondition: libraries for your domain are available
- Or you can write them
yourself
- Or you can write them

Thank you!
Questions?
References
Rust @ Prodrive
By wildarch
Rust @ Prodrive
- 384
