Home Wiki Programming & Logic Control Flow in Rust: Conditions and Loops for Monitoring Systems
Programming & Logic

Control Flow in Rust: Conditions and Loops for Monitoring Systems

if and else: Making Decisions

Every industrial monitoring system relies on decisions: has the temperature exceeded the limit? Is the pressure normal? In Rust, conditions are written with if without parentheses around the condition (unlike C):

let temperature = 85.0;

if temperature > 80.0 {
    println!("Warning: high temperature!");
} else if temperature > 60.0 {
    println!("Normal: temperature within range");
} else {
    println!("Low: check the sensor");
}

Important note: the condition must be of type bool. Rust will not accept if temperature — you must write if temperature > 0.0.

if as an Expression That Returns a Value

In Rust, if is not just a statement — it is an expression that returns a value:

let temperature = 75.0;
let status = if temperature > 80.0 { "critical" } else { "normal" };
println!("Status: {}", status);

This resembles the ternary ? : operator in C, but is clearer to read. The rule: both branches must return the same type.

match: Powerful Pattern Matching

match is Rust's most powerful decision-making tool. It resembles switch in C but is far smarter — the compiler forces you to cover every possibility:

enum MachineState {
    Running,
    Idle,
    Error,
    Maintenance,
}

let state = MachineState::Running;

match state {
    MachineState::Running => println!("Machine operating normally"),
    MachineState::Idle => println!("Machine in idle mode"),
    MachineState::Error => println!("Error! Check immediately"),
    MachineState::Maintenance => println!("Scheduled maintenance"),
}

If you omit any variant, the compiler rejects the code. This guarantees you will never forget to handle an error state in an industrial control system.

match with Values and Ranges

let alarm_level: u8 = 3;

let action = match alarm_level {
    0 => "no alarm",
    1 => "advisory — monitor the situation",
    2 => "warning — intervention needed soon",
    3..=5 => "critical — stop the machine immediately",
    _ => "unknown level",  // _ matches any other value
};

println!("Action: {}", action);

_ is the catch-all pattern — it matches any value not listed. The range 3..=5 matches 3, 4, and 5.

loop: The Infinite Monitoring Cycle

In industrial systems, many programs run indefinitely — reading sensors, checking thresholds, logging data. loop is an infinite loop with the ability to exit via break:

let mut cycle_count = 0;

loop {
    cycle_count += 1;
    let reading = simulate_sensor_read();

    if reading > 100.0 {
        println!("Alarm at cycle {}: reading {:.1}", cycle_count, reading);
        break;
    }

    if cycle_count >= 1000 {
        println!("Scan complete: {} cycles with no alarm", cycle_count);
        break;
    }
}

loop as an Expression That Returns a Value

let final_reading = loop {
    let r = read_sensor();
    if r > 0.0 {
        break r; // break with a value is returned by loop
    }
};
println!("First valid reading: {:.1}", final_reading);

while and while let

while continues as long as the condition is true:

let mut retries = 0;

while retries < 3 {
    match connect_to_plc() {
        Ok(_) => {
            println!("Connected to PLC successfully!");
            break;
        }
        Err(e) => {
            retries += 1;
            println!("Attempt {} failed: {}", retries, e);
        }
    }
}

while let combines while and match — it continues as long as the pattern matches:

let mut sensor_queue = vec![23.5, 24.1, 0.0, 25.3];

while let Some(reading) = sensor_queue.pop() {
    if reading > 0.0 {
        println!("Reading: {:.1}°C", reading);
    }
}
// stops when the queue is empty (pop returns None)

for and Ranges: Iterating Over Sensor Data

for is the most commonly used loop — it iterates over any collection:

let sensors = [101, 102, 103, 104, 105];

for sensor_id in &sensors {
    println!("Reading sensor {}...", sensor_id);
}

Ranges

// from 0 to 9 (excludes 10)
for i in 0..10 {
    println!("Cycle {}", i);
}

// from 1 to 10 (includes 10)
for i in 1..=10 {
    println!("Part number {}", i);
}

enumerate: Index with Value

let readings = [23.5, 24.1, 22.8, 25.0];

for (index, temp) in readings.iter().enumerate() {
    println!("Sensor {}: {:.1}°C", index + 1, temp);
}

Expressions, Not Statements: if Returns a Value

One of Rust's most important features is that most constructs are expressions, not statements. This means they return values:

// if as expression
let message = if temperature > 80.0 {
    "critical"
} else {
    "normal"
};

// match as expression
let priority = match alarm_level {
    0 => "low",
    1 | 2 => "medium",
    _ => "high",
};

// loop can return a value via break
let result = loop {
    let val = compute();
    if val > threshold {
        break val;
    }
};

This reduces the need for temporary mut variables and makes code clearer.

Summary

if/else for simple decisions, match for exhaustive pattern matching that guarantees every case is covered, loop for continuous monitoring cycles, while for conditional repetition, and for for iterating over collections. All of these constructs are expressions that return values — making Rust code concise and safe. In the next lesson, we will learn about functions — how to split a program into reusable units.

if-else match loop while for pattern-matching الشروط الحلقات مطابقة الأنماط التدفق المنطقي حلقة المراقبة القرارات