BasicsImpCodeStory_ControlFlow.html
copyright © James Fawcett
Revised: 05/11/2026
4.0 Prologue
Control flow determines the order in which statements execute. Branching selects one
path from several alternatives; looping repeats a block until a condition changes;
early exit escapes from a loop or function before its natural end. Modern languages
enrich these primitives with pattern matching, making branching both more expressive
and safer.
4.1 if / else
The if statement is universal. In Rust and many functional-influenced
languages it is also an expression that returns a value, removing
the need for a ternary operator:
// Rust (if is an expression)
let label = if score >= 90 { "A" } else { "B" };
// C++ (statement only; ternary for inline)
std::string label = (score >= 90) ? "A" : "B";
// C# (statement; ternary available)
string label = score >= 90 ? "A" : "B";
// Python (inline conditional expression)
label = "A" if score >= 90 else "B"
All four languages support else if chains (Python spells it elif).
When there are many discrete cases, match/switch is cleaner.
4.2 match / switch
A match or switch statement branches on the value of an expression. The key
differences across languages are exhaustiveness and
fall-through:
// Rust (exhaustive, no fall-through, expression)
let msg = match status {
200 => "OK",
404 => "Not Found",
500 => "Server Error",
_ => "Unknown",
};
// C++ (not exhaustive, fall-through unless break)
switch (status) {
case 200: msg = "OK"; break;
case 404: msg = "Not Found"; break;
default: msg = "Unknown";
}
// C# (no implicit fall-through; goto case allowed)
switch (status) {
case 200: msg = "OK"; break;
default: msg = "Unknown"; break;
}
// Python (match added in 3.10; structural pattern matching)
match status:
case 200: msg = "OK"
case 404: msg = "Not Found"
case _: msg = "Unknown"
Rust’s compiler enforces exhaustiveness: every possible value must be covered or
a wildcard _ must appear. This catches missing cases at compile time.
4.3 Loops
Every language provides at least while and for loops.
// Rust: loop (infinite), while, for-in (iterator-based)
loop { if done { break; } }
while !done { /* ... */ }
for x in 0..10 { /* x = 0,1,...,9 */ }
for item in &collection { /* borrow each element */ }
// C++: while, do-while, for, range-for
while (!done) { /* ... */ }
for (int i = 0; i < 10; ++i) { /* ... */ }
for (auto& item : collection) { /* ... */ }
// C#: while, do-while, for, foreach
while (!done) { /* ... */ }
for (int i = 0; i < 10; i++) { /* ... */ }
foreach (var item in collection) { /* ... */ }
// Python: while, for-in (always iterator-based)
while not done: pass
for i in range(10): pass
for item in collection: pass
Rust’s loop keyword creates an explicit infinite loop; the compiler
knows it never falls through, enabling break with a value (see below).
Range-based and iterator-based for loops are idiomatic in all modern languages.
4.4 break and continue
break exits the innermost enclosing loop. continue skips
the remainder of the current iteration.
Rust adds two notable extensions:
-
break with a value:
loop { break expr; } returns
expr from the loop, making the loop an expression.
-
labeled break/continue: labels (
'outer: loop { ... break 'outer; })
allow targeting an outer loop rather than the innermost one. C# and Java support
labeled break; C++ and Python do not.
// Rust: labeled break targeting outer loop
'outer: for i in 0..5 {
for j in 0..5 {
if i + j == 6 { break 'outer; }
}
}
4.5 Pattern Matching
Pattern matching extends matching to the structure of a value, not just
its identity. It can destructure tuples, structs, and enums, bind sub-values to names,
and apply guards.
// Rust: destructuring in match
match point {
(0, 0) => println!("origin"),
(x, 0) => println!("on x-axis at {x}"),
(0, y) => println!("on y-axis at {y}"),
(x, y) => println!("at ({x}, {y})"),
}
// Rust: matching on enum variants with data
match msg {
Message::Quit => quit(),
Message::Move { x, y } => move_to(x, y),
Message::Write(text) => print!("{text}"),
}
// Python 3.10+ structural pattern matching
match command.split():
case ["go", direction]: move(direction)
case ["pick", item]: pick_up(item)
case _: print("unknown")
Guards add a boolean condition to a pattern arm:
Some(x) if x > 0 => positive(x).
C#’s switch expression (C# 8+) provides similar power through
when clauses.
4.6 Epilogue
Pattern matching turns match/switch from a simple integer branch into a structural
decomposition tool. Languages that enforce exhaustiveness shift a class of runtime
errors to compile time. The next chapter covers functions - the primary
unit of code reuse.
4.7 References
Rust match Control Flow - The Book
C++ switch Statement - cppreference
C# Selection Statements - Microsoft
PEP 634 - Structural Pattern Matching