if / else in Rust
Conditional logic is one of the most fundamental tools in any programming language.
Rust's if and else are familiar in syntax but have two important differences
from languages like JavaScript, Python, or C: conditions must be a bool (there
is no implicit truthiness), and if is an expression that can return a value.
Basic if / else
The simplest form of a conditional checks one condition and runs a block of code when it is true, with an optional fallback for when it is false.
fn main() {
let temperature = 28;
if temperature > 30 {
println!("It is hot outside.");
} else {
println!("The weather is pleasant.");
}
}The weather is pleasant.
Conditions Must Be bool — No Implicit Truthiness
This is one of the biggest differences from Python and JavaScript. In Rust, the
condition of an if expression must evaluate to a bool. Rust will not
treat 0 as false or a non-empty string as true — you must write the comparison
explicitly.
fn main() {
let count = 5;
// CORRECT — explicit comparison produces a bool
if count > 0 {
println!("count is positive");
}
// WRONG — this does not compile in Rust
// if count { // ERROR: expected bool, found integer
// println!("this fails");
// }
let name = String::from("Alice");
// CORRECT — .is_empty() returns a bool
if !name.is_empty() {
println!("Hello, {}!", name);
}
// WRONG — strings are not truthy in Rust
// if name { // ERROR: expected bool, found struct String
// println!("this also fails");
// }
}else if Chains
You can chain multiple conditions together with else if. Rust evaluates them in
order and runs the first block whose condition is true.
fn main() {
let score = 72;
if score >= 90 {
println!("Grade: A");
} else if score >= 80 {
println!("Grade: B");
} else if score >= 70 {
println!("Grade: C");
} else if score >= 60 {
println!("Grade: D");
} else {
println!("Grade: F");
}
}Grade: C
if as an Expression
In Rust, if is an expression — it produces a value that can be assigned
to a variable, returned from a function, or used inline. This eliminates the need
for ternary operators (? :) found in C, Java, and JavaScript.
fn main() {
let condition = true;
// if as an expression — assign the result directly
let number = if condition { 5 } else { 10 };
println!("number = {}", number); // number = 5
// Equivalent to a ternary in other languages:
// let number = condition ? 5 : 10; (JavaScript / C / Java)
// Use it inline in a function call
let age = 20;
println!("You are {}.", if age >= 18 { "an adult" } else { "a minor" });
}All Arms Must Return the Same Type
When if is used as an expression, every branch must produce the same type.
Rust is statically typed and must know at compile time what type a variable holds.
If the arms return different types, the compiler will reject the code.
fn main() {
let condition = true;
// This is FINE — both arms are i32
let x = if condition { 5 } else { 10 };
// This DOES NOT COMPILE — one arm is i32, the other is &str
// let y = if condition { 5 } else { "ten" };
// ERROR:
// if and else have incompatible types
// expected integer, found &str
}if Without else as a Statement
When if is used as a statement (not assigned to a variable), the else
branch is optional. The block implicitly returns () (the unit type), so there
is no type mismatch.
fn main() {
let logged_in = true;
// No else needed — we only care about the true case
if logged_in {
println!("Welcome back!");
}
// The compiler infers this is a statement (returns unit), not an expression
// so no else branch is required
}if in Return Statements
Because if is an expression, functions can use it as their final (returned)
value without writing return.
fn classify_number(n: i32) -> &'static str {
// The if expression is the last thing in the function
// so its value is automatically returned
if n > 0 {
"positive"
} else if n < 0 {
"negative"
} else {
"zero"
}
}
fn abs_value(n: i32) -> i32 {
if n < 0 { -n } else { n }
}
fn main() {
println!("{}", classify_number(42)); // positive
println!("{}", classify_number(-7)); // negative
println!("{}", classify_number(0)); // zero
println!("{}", abs_value(-15)); // 15
}Combining if with let Bindings
You can use if together with let for concise conditional assignments.
A common pattern is to derive a value from a condition and immediately use it.
fn main() {
let is_weekend = true;
let day_type = if is_weekend { "weekend" } else { "weekday" };
println!("Today is a {}.", day_type);
// Combining multiple conditions in one let binding
let hour = 14;
let greeting = if hour < 12 {
"Good morning"
} else if hour < 18 {
"Good afternoon"
} else {
"Good evening"
};
println!("{}!", greeting); // Good afternoon!
// Nested if in a let binding (keep it readable)
let speed = 85;
let speed_limit = 100;
let within_limit = if speed <= speed_limit { true } else { false };
// Simpler: let within_limit = speed <= speed_limit;
println!("Within limit: {}", within_limit);
}Nested if Expressions
You can nest if expressions inside one another. Be mindful of readability —
deeply nested ifs are usually a sign that the logic should be refactored into
a separate function or a match expression.
fn can_buy_alcohol(age: u32, has_id: bool) -> bool {
if age >= 18 {
if has_id {
true
} else {
println!("Age OK but no ID presented.");
false
}
} else {
println!("Under age.");
false
}
}
fn main() {
println!("{}", can_buy_alcohol(20, true)); // true
println!("{}", can_buy_alcohol(20, false)); // false (prints warning)
println!("{}", can_buy_alcohol(16, true)); // false (prints warning)
}Practical Examples
Here are a few real-world patterns that combine if/else with Rust's type system.
fn divide(a: f64, b: f64) -> Option<f64> {
if b == 0.0 {
None // cannot divide by zero
} else {
Some(a / b)
}
}
fn fizzbuzz(n: u32) -> String {
if n % 15 == 0 {
String::from("FizzBuzz")
} else if n % 3 == 0 {
String::from("Fizz")
} else if n % 5 == 0 {
String::from("Buzz")
} else {
n.to_string()
}
}
fn bmi_category(bmi: f64) -> &'static str {
if bmi < 18.5 {
"Underweight"
} else if bmi < 25.0 {
"Normal weight"
} else if bmi < 30.0 {
"Overweight"
} else {
"Obese"
}
}
fn main() {
println!("{:?}", divide(10.0, 3.0)); // Some(3.3333...)
println!("{:?}", divide(10.0, 0.0)); // None
for i in 1..=15 {
print!("{} ", fizzbuzz(i));
}
println!();
// 1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz
println!("{}", bmi_category(22.4)); // Normal weight
}Comparison with Other Languages
Language | Implicit truthiness? | if as expression? | Ternary operator? |
|---|---|---|---|
Rust | No — bool only | Yes | No (use if/else) |
JavaScript | Yes (0, "", null...) | No | Yes (? :) |
Python | Yes (0, [], None...) | Yes (ternary only) | Yes (x if c else y) |
Java | No — bool only | No | Yes (? :) |
C | Yes (0 = false) | No | Yes (? :) |
Go | No — bool only | No | No |