7.1 Conditional Statements
Conditional statements allow your program to make decisions based on specific criteria. Rust's primary decision-making construct is the if
statement, similar to C's, but with some key differences.
7.1.1 Conditions Must Be Boolean
In Rust, conditions in if
statements must explicitly be of type bool
. Unlike C, where any non-zero integer is considered true
, Rust does not perform implicit conversions from integers or other types to bool
.
Comparison
C Code:
int number = 5;
if (number) {
printf("Number is non-zero.\n");
}
In C, number
being non-zero evaluates to true
.
Rust Equivalent:
fn main() { let number = 5; if number != 0 { println!("Number is non-zero."); } }
In Rust, you must explicitly compare number
to zero to produce a bool
.
Note: Attempting to use a non-boolean condition in Rust will result in a compile-time error, making your code safer by preventing unintended truthy or falsy evaluations.
7.1.2 The if
Statement
The if
statement in Rust executes code based on a condition that evaluates to true
.
fn main() { let number = 5; if number > 0 { println!("The number is positive."); } }
Key Points:
- No Parentheses Required: Parentheses around the condition are optional in Rust.
- Braces Are Required: Even for single-line bodies, braces
{}
are required.
Comparison with C
C Code:
int number = 5;
if (number > 0) {
printf("The number is positive.\n");
}
In C, parentheses around the condition are required, but braces are optional for single statements.
7.1.3 else if
and else
You can extend if
statements with else if
and else
clauses to handle multiple conditions.
fn main() { let number = 0; if number > 0 { println!("The number is positive."); } else if number < 0 { println!("The number is negative."); } else { println!("The number is zero."); } }
Key Points:
- Conditions Checked Sequentially: Conditions are evaluated from top to bottom.
- Exclusive Execution: Only the first branch where the condition evaluates to
true
is executed. If none of the conditions are met, the optionalelse
branch is executed. - Syntax Simplicity: No parentheses are needed around conditions, and Rust does not require
{}
betweenelse
andif
.
Comparison with C
C Code:
int number = 0;
if (number > 0) {
printf("The number is positive.\n");
} else if (number < 0) {
printf("The number is negative.\n");
} else {
printf("The number is zero.\n");
}
Note: In C, both parentheses around conditions and braces for code blocks are required by syntax rules.
7.1.4 if
as an Expression
In Rust, if
statements can be used as expressions that return values. This allows you to assign the result of an if
expression to a variable.
fn main() { let condition = true; let number = if condition { 10 } else { 20 }; println!("The number is: {}", number); }
Key Points:
- Expression-Based: Both
if
andelse
branches must return values. - Type Consistency: All branches must return values of the same type.
- No Ternary Operator: Rust uses
if
expressions instead of the ternary operator found in C.
When using if
as an expression to assign a value, Rust requires that all possible conditions are covered. This means that you must include an else
clause. Without an else
clause, the if
expression might not return a value in some cases, leading to a compile-time error.
Comparison with the Ternary Operator in C
C Code:
int condition = 1; // true
int number = condition ? 10 : 20;
printf("The number is: %d\n", number);
7.1.5 Type Consistency in if
Expressions
All branches of an if
expression must return values of the same type.
fn main() { let condition = true; let number = if condition { 5 } else { "six" // Error: mismatched types }; }
Error:
error[E0308]: if and else have incompatible types
Explanation: The if
branch returns an i32
, but the else
branch returns a &str
. Rust's type system enforces consistency to prevent runtime errors.
7.1.6 The match
Statement
Rust's match
statement is a powerful control flow construct for pattern matching. It is more versatile than C's switch
statement.
fn main() { let number = 2; match number { 1 => println!("One"), 2 => println!("Two"), 3 => println!("Three"), _ => println!("Other"), } }
Key Points:
- Patterns:
match
can handle a wide range of patterns. - Exhaustiveness Checking: The compiler ensures all possible cases are covered.
- Wildcard Pattern
_
: Acts as a catch-all, similar todefault
in C.
Comparison with C's switch
C Code:
int number = 2;
switch (number) {
case 1:
printf("One\n");
break;
case 2:
printf("Two\n");
break;
default:
printf("Other\n");
break;
}
Advantages of Rust's match
:
- No Fall-Through: Each arm is independent; there's no implicit fall-through.
- Pattern Matching: Can match on more complex patterns, including ranges and destructured data.
We will explore Rust's powerful pattern matching and the match
statement in full detail in a later chapter.