Home Wiki Programming & Logic Rust for Industrial Systems
Programming & Logic

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>: either Some(value) or None
  • Result<T, E>: either Ok(value) or Err(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.

Rust ownership borrowing async Modbus memory-safety لغة رست الملكية والاستعارة البرمجة غير المتزامنة أمان الذاكرة الأنظمة المدمجة Tokio