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 memoryString: 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?
constfor calibration values and thresholds (like temperature limits)staticwhen 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.