Home Wiki Programming & Logic Variables and Types in Rust: The Foundation of Every Industrial Program
Programming & Logic

Variables and Types in Rust: The Foundation of Every Industrial Program

Variables: Immutable by Default

In most programming languages, variables are mutable automatically. Rust flips this convention: every variable is immutable unless you explicitly request mutability. Why? Because data that does not change is easier to reason about and safer — especially in industrial environments where a sensor reading should not be modified after it is recorded.

fn main() {
    let sensor_id = 42;       // immutable — cannot change
    // sensor_id = 43;        // compile error! cannot modify immutable variable

    let mut temperature = 23.5; // mutable — can be modified
    temperature = 24.1;         // allowed
    println!("Sensor {}: {:.1}°C", sensor_id, temperature);
}

The rule is simple: use let for immutable bindings (the default) and let mut for variables you need to modify.

Primitive Types: Integers, Floats, and Booleans

Rust is a statically typed language — every value has a type known at compile time. However, the compiler is smart enough to infer types automatically in most cases.

Integer Types

Type Size Range Industrial Use
i8 8-bit -128 to 127 Small control signals
u8 8-bit 0 to 255 Modbus byte
i16 16-bit -32768 to 32767 Signed sensor register
u16 16-bit 0 to 65535 Raw Modbus register
i32 32-bit ±2 billion Default integer
u32 32-bit 0 to 4 billion Production counter
i64 64-bit ±9.2 × 10¹⁸ Timestamps
u64 64-bit 0 to 18.4 × 10¹⁸ Unique identifiers
let modbus_register: u16 = 0xFFFF;   // Modbus register: unsigned 16-bit
let production_count: u32 = 150_000; // production counter — underscores for readability
let sensor_reading: i16 = -150;      // sensor reading that can be negative

Floating-Point Types

Type Precision Use Case
f32 32-bit (~7 digits) When memory is limited
f64 64-bit (~15 digits) Default — more precise
let temperature: f64 = 36.6;     // high-precision temperature
let pressure: f32 = 1013.25;     // atmospheric pressure — f32 precision is sufficient

Booleans

let is_running: bool = true;      // is the machine running?
let alarm_active = false;          // type inferred as bool

Characters and Strings

Rust distinguishes between a character and a string:

let grade: char = 'A';           // single character — single quotes
let status: &str = "Running";    // string literal — a reference to a string
let name: String = String::from("CNC-Mill-07"); // owned string — can be modified
  • &str (string slice): an immutable string stored in read-only memory
  • String: a heap-allocated string that you can grow and modify
let mut log_entry = String::from("Machine started");
log_entry.push_str(" at 08:00");  // append text
println!("{}", log_entry);        // Machine started at 08:00

Constants and Statics

Beyond let, Rust provides two ways to define values that never change:

// const: compile-time constant — inlined wherever used
const MAX_TEMPERATURE: f64 = 120.0;
const SENSOR_COUNT: usize = 256;

// static: lives for the entire program — has a fixed memory address
static FACTORY_NAME: &str = "Dr. Machine Plant Alpha";

When to use each?

  • const for calibration values and thresholds (like temperature limits)
  • static when you need a fixed memory address (rare in practice)

Type Annotations and Inference

The compiler infers types in most situations:

let x = 5;          // inferred: i32
let y = 3.14;       // inferred: f64
let active = true;  // inferred: bool

But sometimes you must annotate explicitly:

// When parsing a string to a number — the compiler does not know which number type
let value: u16 = "1024".parse().expect("Not a number");

// When creating an empty collection — no element to infer from
let readings: Vec<f64> = Vec::new();

Shadowing: Reusing Names Safely

Rust allows shadowing — redeclaring a variable with the same name:

let reading = "42.5";                        // type: &str
let reading: f64 = reading.parse().unwrap(); // type: f64 — shadows the first
let reading = reading * 1.8 + 32.0;         // now in Fahrenheit

println!("Reading: {:.1}°F", reading);

Shadowing differs from mut:

  • mut: same type, same variable, different value
  • Shadowing: can change the type entirely — creates a new variable

This is extremely useful in industrial data pipelines: you take a raw reading as a string, parse it to a number, then calibrate it — each time using the same logical name.

Summary

Variables in Rust are immutable by default — add mut only when needed. The type system is strict but smart: it infers types automatically and catches errors at compile time. Primitive types (integers, floats, booleans, and strings) cover everything you need for processing sensor and machine data. In the next lesson, we will learn how programs make decisions and repeat operations — conditions and loops.

variables types mutability integers floating-point boolean المتغيرات الأنواع الثوابت الأعداد الصحيحة الأعداد العشرية نظام الأنواع