if Expressions
An if expression runs a block of code only when a condition is true. The condition is evaluated once. If it is true, the block runs. If it is false, the block is skipped and execution continues after the if.
fn main() {
let temperature = 31;
if temperature > 30 {
println!("It is hot.");
}
if temperature < 0 {
println!("It is freezing.");
}
println!("Done.");
}
It is hot.
Done.
Syntax
if <condition> {
<block>
}
+-----------+
| condition |
+-----+-----+
|
+-------+-------+
| |
true false
| |
+----v----+ |
| block | |
+----+----+ |
| |
+-------+-------+
|
next statement
The parts of an if:
| Part | Rule |
|---|---|
if keyword |
Starts the expression |
| Condition | Any expression of type bool |
| Block | Curly braces are mandatory, even for a single statement |
| Parentheses | Not required around the condition |
The Condition Must Be a bool
Rust does not convert values to booleans. The condition must have type bool exactly. Integers, strings, Option values, and empty collections are never treated as "truthy" or "falsy".
// ❌ does not compile
fn main() {
let number = 3;
if number {
println!("number was three");
}
}
error[E0308]: mismatched types
--> main.rs:4:8
|
4 | if number {
| ^^^^^^ expected `bool`, found integer
Write the comparison you mean:
| Other languages | Rust |
|---|---|
if (count) |
if count != 0 |
if (name) or if (!name.empty()) |
if !name.is_empty() |
if (ptr) |
if option.is_some() |
if (flag == true) |
if flag |
fn main() {
let count = 0;
let name = "";
if count != 0 {
println!("count is non-zero");
}
if !name.is_empty() {
println!("name is set");
}
println!("checked");
}
checked
Neither condition is true, so only the last line prints. When a variable is already a bool, use it directly. if flag == true is redundant, and clippy flags it.
Building Conditions
Any expression that produces a bool is a valid condition: comparisons, logical operators, method calls, function calls, and bool variables.
| Form | Example |
|---|---|
| Comparison | age >= 18 |
| Logical AND | age >= 18 && has_ticket |
| Logical OR | age < 13 || age > 65 |
| Logical NOT | !is_banned |
Method returning bool |
name.starts_with("Fer") |
| Closure-based check | digits.chars().all(|c| c.is_ascii_digit()) |
bool variable |
has_ticket |
fn main() {
let age = 25;
let has_ticket = true;
let is_banned = false;
let name = String::from("Ferris");
let digits = "2024";
let maybe: Option<i32> = Some(7);
if age >= 18 && has_ticket && !is_banned {
println!("entry allowed");
}
if age < 13 || age > 65 {
println!("discounted ticket");
}
if name.starts_with("Fer") {
println!("name starts with Fer");
}
if digits.chars().all(|c| c.is_ascii_digit()) {
println!("all digits");
}
if maybe.is_some() {
println!("maybe holds a value");
}
if name.is_empty() {
println!("never printed");
}
}
entry allowed
name starts with Fer
all digits
maybe holds a value
Both Sides of a Comparison Must Have the Same Type
Comparison operators do not convert between numeric types. Comparing an integer with a float, or an i32 with an i64, is a compile-time error.
// ❌ does not compile
fn main() {
let x = 5;
if x == 5.0 {
println!("five");
}
}
error[E0277]: can't compare `{integer}` with `{float}`
--> main.rs:4:10
|
4 | if x == 5.0 {
| ^^ no implementation for `{integer} == {float}`
|
= help: the trait `PartialEq<{float}>` is not implemented for `{integer}`
// ❌ does not compile
fn main() {
let small: i32 = 5;
let big: i64 = 5;
if small == big {
println!("equal");
}
}
error[E0308]: mismatched types
--> main.rs:5:17
|
5 | if small == big {
| ----- ^^^ expected `i32`, found `i64`
| |
| expected because this is `i32`
|
help: you can convert `small` from `i32` to `i64`, matching the type of `big`
|
5 | if i64::from(small) == big {
| ++++++++++ +
Convert one side explicitly:
fn main() {
let small: i32 = 5;
let big: i64 = 5;
if i64::from(small) == big {
println!("equal");
}
}
equal
The Condition Is Evaluated Once, Left to Right
When a condition contains && or ||, evaluation stops as soon as the result is known. The right-hand operand of && is skipped when the left side is false. The right-hand operand of || is skipped when the left side is true.
fn is_ready(label: &str, answer: bool) -> bool {
println!("checking {label}");
answer
}
fn main() {
if is_ready("first", true) && is_ready("second", false) && is_ready("third", true) {
println!("all ready");
} else {
println!("not ready");
}
if is_ready("a", true) || is_ready("b", true) {
println!("at least one ready");
}
}
checking first
checking second
not ready
checking a
at least one ready
"third" and "b" are never checked. Functions with side effects inside a condition run only when the evaluation reaches them.
Braces Are Required
The block after the condition must be wrapped in { }. Rust has no single-statement form like if (x) statement; in C.
// ❌ does not compile
fn main() {
let score = 82;
if score >= 50
println!("pass");
}
error: expected `{`, found `println`
--> main.rs:5:9
|
5 | println!("pass");
| ^^^^^^^ expected `{`
|
note: the `if` expression is missing a block after this condition
--> main.rs:4:8
|
4 | if score >= 50
| ^^^^^^^^^^^
help: you might have meant to write this as part of a block
|
5 | { println!("pass"); }
| + +
Mandatory braces remove a whole category of bugs in which a second indented line looks like part of the if but is not.
Parentheses Are Not Needed
The condition does not need parentheses. Adding them compiles, but the compiler warns about them.
fn main() {
let score = 82;
if (score >= 50) {
println!("pass");
}
}
warning: unnecessary parentheses around `if` condition
--> main.rs:4:8
|
4 | if (score >= 50) {
| ^ ^
|
= note: `#[warn(unused_parens)]` (part of `#[warn(unused)]`) on by default
help: remove these parentheses
|
4 - if (score >= 50) {
4 + if score >= 50 {
|
Parentheses are still useful inside a condition to group && and ||, for example if (a || b) && c. Only the outermost pair around the whole condition is flagged.
An if Without else Evaluates to ()
An if is an expression, so it has a value. When there is no else branch, the value is always the unit value (). This is true whether the condition was true or false.
fn main() {
let x = 5;
let result = if x > 3 {
println!("x is greater than 3");
};
println!("{result:?}");
let skipped = if x > 10 {
println!("never printed");
};
println!("{skipped:?}");
}
x is greater than 3
()
()
Because the missing else branch can only produce (), the block of an if without else must also produce (). A block that ends in a value, such as { 5 }, is rejected.
if as a Statement
Most if expressions are used as statements: the value is () and is discarded. An if in statement position does not need a trailing semicolon. A semicolon after the closing brace is allowed and has no effect.
fn main() {
let n = 7;
if n % 2 == 1 {
println!("odd");
};
if n > 5 {
println!("big");
}
println!("end");
}
odd
big
end