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.