blob: 37e7d707ca72e6774f090863fd5a9ca6264dc8e5 [file] [log] [blame] [view]
r[expr.if]
# `if` expressions
r[expr.if.syntax]
```grammar,expressions
IfExpression ->
`if` Conditions BlockExpression
(`else` ( BlockExpression | IfExpression ) )?
Conditions ->
Expression _except [StructExpression]_
| LetChain
LetChain -> LetChainCondition ( `&&` LetChainCondition )*
LetChainCondition ->
Expression _except [ExcludedConditions]_
| OuterAttribute* `let` Pattern `=` Scrutinee _except [ExcludedConditions]_
@root ExcludedConditions ->
StructExpression
| LazyBooleanExpression
| RangeExpr
| RangeFromExpr
| RangeInclusiveExpr
| AssignmentExpression
| CompoundAssignmentExpression
```
<!-- TODO: The struct exception above needs clarification, see https://github.com/rust-lang/reference/issues/1808
The chain grammar could use some work, see https://github.com/rust-lang/reference/issues/1811
-->
r[expr.if.intro]
The syntax of an `if` expression is a sequence of one or more condition operands separated by `&&`,
followed by a consequent block, any number of `else if` conditions and blocks, and an optional trailing `else` block.
r[expr.if.condition]
Condition operands must be either an [_Expression_] with a [boolean type] or a conditional `let` match.
r[expr.if.condition-true]
If all of the condition operands evaluate to `true` and all of the `let` patterns successfully match their [scrutinee]s,
the consequent block is executed and any subsequent `else if` or `else` block is skipped.
r[expr.if.else-if]
If any condition operand evaluates to `false` or any `let` pattern does not match its scrutinee,
the consequent block is skipped and any subsequent `else if` condition is evaluated.
r[expr.if.else]
If all `if` and `else if` conditions evaluate to `false` then any `else` block is executed.
r[expr.if.result]
An `if` expression evaluates to the same value as the executed block, or `()` if no block is evaluated.
r[expr.if.type]
An `if` expression must have the same type in all situations.
```rust
# let x = 3;
if x == 4 {
println!("x is four");
} else if x == 3 {
println!("x is three");
} else {
println!("x is something else");
}
// `if` can be used as an expression.
let y = if 12 * 15 > 150 {
"Bigger"
} else {
"Smaller"
};
assert_eq!(y, "Bigger");
```
r[expr.if.let]
## `if let` patterns
r[expr.if.let.intro]
`let` patterns in an `if` condition allow binding new variables into scope when the pattern matches successfully.
The following examples illustrate bindings using `let` patterns:
```rust
let dish = ("Ham", "Eggs");
// This body will be skipped because the pattern is refuted.
if let ("Bacon", b) = dish {
println!("Bacon is served with {}", b);
} else {
// This block is evaluated instead.
println!("No bacon will be served");
}
// This body will execute.
if let ("Ham", b) = dish {
println!("Ham is served with {}", b);
}
if let _ = 5 {
println!("Irrefutable patterns are always true");
}
```
r[expr.if.let.or-pattern]
Multiple patterns may be specified with the `|` operator.
This has the same semantics as with `|` in [`match` expressions]:
```rust
enum E {
X(u8),
Y(u8),
Z(u8),
}
let v = E::Y(12);
if let E::X(n) | E::Y(n) = v {
assert_eq!(n, 12);
}
```
r[expr.if.chains]
## Chains of conditions
r[expr.if.chains.intro]
Multiple condition operands can be separated with `&&`.
r[expr.if.chains.order]
Similar to a `&&` [_LazyBooleanOperatorExpression_], each operand is evaluated from left-to-right until an operand evaluates as `false` or a `let` match fails,
in which case the subsequent operands are not evaluated.
r[expr.if.chains.bindings]
The bindings of each pattern are put into scope to be available for the next condition operand and the consequent block.
The following is an example of chaining multiple expressions, mixing `let` bindings and boolean expressions, and with expressions able to reference pattern bindings from previous expressions:
```rust
fn single() {
let outer_opt = Some(Some(1i32));
if let Some(inner_opt) = outer_opt
&& let Some(number) = inner_opt
&& number == 1
{
println!("Peek a boo");
}
}
```
The above is equivalent to the following without using chains of conditions:
```rust
fn nested() {
let outer_opt = Some(Some(1i32));
if let Some(inner_opt) = outer_opt {
if let Some(number) = inner_opt {
if number == 1 {
println!("Peek a boo");
}
}
}
}
```
r[expr.if.chains.or]
If any condition operand is a `let` pattern, then none of the condition operands can be a `||` [lazy boolean operator expression][_LazyBooleanOperatorExpression_] due to ambiguity and precedence with the `let` scrutinee.
If a `||` expression is needed, then parentheses can be used. For example:
```rust
# let foo = Some(123);
# let condition1 = true;
# let condition2 = false;
// Parentheses are required here.
if let Some(x) = foo && (condition1 || condition2) { /*...*/ }
```
r[expr.if.edition2024]
> [!EDITION-2024]
> Before the 2024 edition, let chains are not supported. That is, the [LetChain] grammar is not allowed in an `if` expression.
[_BlockExpression_]: block-expr.md
[_Expression_]: ../expressions.md
[_LazyBooleanOperatorExpression_]: operator-expr.md#lazy-boolean-operators
[_Pattern_]: ../patterns.md
[_Scrutinee_]: match-expr.md
[`match` expressions]: match-expr.md
[boolean type]: ../types/boolean.md
[scrutinee]: ../glossary.md#scrutinee
<script>
(function() {
var fragments = {
"#if-let-expressions": "if-expr.html#if-let-patterns",
};
var target = fragments[window.location.hash];
if (target) {
var url = window.location.toString();
var base = url.substring(0, url.lastIndexOf('/'));
window.location.replace(base + "/" + target);
}
})();
</script>