Rust for Industrial Systems
Why Rust for Industry?
Imagine running a production line with 200 temperature, pressure, and vibration sensors — and the control software crashes due to a null pointer. The machines keep spinning with no monitoring. This scenario happens daily in systems written in C and C++. Rust was built to eliminate this class of problems entirely.
Rust delivers memory safety without a garbage collector. The compiler catches memory bugs — leaks, dangling pointers, data races — before the program ever runs. In industrial environments, this means zero surprise crashes at runtime. This is why Dr. Machine chose Rust to build its entire industrial ERP platform.
Ownership and Borrowing: Safety at Zero Cost
The ownership system is the heart of Rust. The rule is simple: every value in memory has exactly one owner. When the owner goes out of scope, the memory is freed automatically.
fn process_reading() {
let data = vec![23.5, 24.1, 22.8]; // data owns this Vec
analyze(data); // ownership moves to analyze
// data cannot be used here — the compiler prevents it
}
Borrowing allows temporary access without transferring ownership. The compiler enforces that you cannot have a mutable reference while immutable references exist — preventing data races at compile time.
Error Handling: No Surprise Crashes
Rust has no null. Instead, it uses two types:
Option<T>: eitherSome(value)orNoneResult<T, E>: eitherOk(value)orErr(error)
fn read_sensor(id: u8) -> Result<f64, SensorError> {
let raw = hardware_read(id)?; // ? propagates errors
if raw == 0xFFFF {
return Err(SensorError::Disconnected);
}
Ok(raw as f64 / 10.0)
}
The compiler forces you to handle every error case. You cannot ignore a Result — your industrial software will never crash from an unhandled error.
Async Programming: Thousands of Sensors
A modern factory may have thousands of sensors. Reading them sequentially is too slow. Async programming with async/await and the tokio runtime lets you read hundreds of sensors concurrently on a single thread:
use tokio::time::{interval, Duration};
async fn monitor_all_sensors(sensors: Vec<u8>) {
let mut tick = interval(Duration::from_secs(1));
loop {
tick.tick().await;
let tasks: Vec<_> = sensors.iter()
.map(|&id| tokio::spawn(read_sensor(id)))
.collect();
for task in tasks {
match task.await.unwrap() {
Ok(temp) => log_value(temp),
Err(e) => alert_operator(e),
}
}
}
}
This reads all sensors in parallel every second without spawning heavy OS threads.
Example: Reading a Temperature Sensor via Modbus
Modbus RTU is the most widely used protocol in factories. Here is a real-world example reading a PT100 temperature sensor at slave address 3, register 40001:
use tokio_modbus::prelude::*;
use tokio_serial::SerialPortBuilderExt;
async fn read_temperature() -> Result<f64, Box<dyn std::error::Error>> {
let serial = tokio_serial::new("/dev/ttyUSB0", 9600)
.open_native_async()?;
let mut ctx = rtu::attach_slave(serial, Slave(3));
let response = ctx.read_holding_registers(0x0000, 1).await?;
let temperature = response[0] as f64 / 10.0;
println!("Temperature: {:.1}°C", temperature);
Ok(temperature)
}
The ? operator handles every possible failure — serial port, connection, read — in a single character. No unchecked exceptions, no silent failures.
Rust vs C++ in Industrial Environments
| Criterion | Rust | C++ |
|---|---|---|
| Memory safety | Guaranteed at compile time | Programmer's responsibility |
| Dangling pointers | Impossible | Common cause of crashes |
| Data races | Prevented by compiler | Detected at runtime (if lucky) |
| Performance | Equivalent to C++ | Performance baseline |
| Compile time | Slower | Faster |
| Industrial libraries | Growing (tokio-modbus, embedded-hal) | Mature and extensive |
| Learning curve | Steep initially | Moderate, but bugs surface later |
Performance is practically equivalent — the key difference is that Rust prevents entire classes of bugs that cause production downtime in C++ systems. Rust also supports embedded systems via no_std, letting you write firmware for ARM microcontrollers with the same safety guarantees.
Summary
Rust combines C++ performance with memory safety no other systems language offers. The ownership system prevents leaks and dangling pointers at compile time. Result and Option types eliminate null pointer crashes. Async programming with tokio enables efficient monitoring of thousands of sensors. In industrial environments where downtime costs thousands per minute, these guarantees are not a luxury — they are a necessity.