Introduction: Code Blocks and Expressions#
In Rust, a code block is a fundamental syntactic unit. It does not merely group statements together; it is an expression that yields a value.
- Syntax: Enclosed by curly braces
{ ... }. Inside, statements typically terminate with a semicolon;. - Return Value:
- If the final statement of the block is an expression without a trailing semicolon, its evaluated value becomes the return value of the entire block.
- If the final statement ends with a semicolon
;(or if the block is empty), the block evaluates to the unit type()(an empty tuple).
Let’s explore code blocks, if-else expressions, and pattern matching in the interactive slides below:
Conditionals and Branching#
Basic if and else#
The if construct checks a boolean predicate and executes the corresponding branch.
- Syntax Rules:
- The condition must not be wrapped in parentheses
( ). Adding parentheses around the condition (e.g.,if (a > 5)) is considered non-idiomatic and triggers compiler linter warnings. - The condition expression must strictly evaluate to the
booltype. Using integers (like in C or C++) triggers a compilation error.
- The condition must not be wrapped in parentheses
// COMPILE ERROR: count is not a boolean
if count { ... }
// CORRECT: expressions evaluate to bool
if count > 0 { ... }
if count != 0 { ... }Comparison and Logical Operators#
Used to construct conditions:
>(greater than),>=(greater than or equal to)<(less than),<=(less than or equal to)==(equality),!=(inequality)&&(logical AND),||(logical OR)
Assigning from if Expressions#
In Rust, if-else is an expression, which means it returns a value that can be directly bound to a variable.
- Critical Type Rule: All branches in an
if-elseexpression must evaluate to the exact same type. - Rust does not have a traditional ternary operator (
? :). Instead,if-elseserves this role cleanly.
// Correct: both branches produce i32
let y = if x > 100 { 2024 } else { 2025 };
// COMPILE ERROR: mismatched branch types (i32 vs &str):
let y = if x > 100 { 2024 } else { "2025" };
If the else branch is omitted, the if expression must return the unit type (), since evaluating to false produces () by default:
let u = if x > 100 { foo() }; // Valid only if foo() returns ()
Pattern Matching with if let#
Basic if let#
if let provides a concise alternative to match when you only care about handling a single specific pattern.
// For Option
if let Some(x) = foo_opt() {
bar(x); // x is scoped exclusively to this block
} else {
// Executed if the result is None
}Chained if let (let-chains)#
Starting with modern Rust versions, chaining multiple let pattern bindings and boolean conditions using the && operator is supported within a single if statement.
// Pattern matching combined with an additional predicate on x
if let Some(x) = foo() && x > 100 {
todo!()
}
// Multiple pattern matches in a single condition
if let Some(x) = foo() && let Some(y) = bar() && x > y {
todo!()
}Test Your Knowledge#
Take a quick interactive quiz to solidify your understanding of code blocks and branching in Rust: