Skip to content

match and if let md update #1623

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 71 additions & 102 deletions src/flow_control/if_let.md
Original file line number Diff line number Diff line change
@@ -1,124 +1,93 @@
# if let

For some use cases, when matching enums, `match` is awkward. For example:

```rust
// Make `optional` of type `Option<i32>`
let optional = Some(7);

match optional {
Some(i) => {
println!("This is a really long string and `{:?}`", i);
// ^ Needed 2 indentations just so we could destructure
// `i` from the option.
},
_ => {},
// ^ Required because `match` is exhaustive. Doesn't it seem
// like wasted space?
};
# if let Expressions

```
Syntax
IfLetExpression :
if let Pattern = Scrutineeexcept lazy boolean operator expression BlockExpression
(else ( BlockExpression | IfExpression | IfLetExpression ) )?

`if let` is cleaner for this use case and in addition allows various
failure options to be specified:
An if let expression is semantically similar to an if expression but in place of a condition operand it expects the keyword let followed by a pattern, an = and a scrutinee operand. If the value of the scrutinee matches the pattern, the corresponding block will execute. Otherwise, flow proceeds to the following else block if it exists. Like if expressions, if let expressions have a value determined by the block that is evaluated.

```rust,editable
fn main() {
// All have type `Option<i32>`
let number = Some(7);
let letter: Option<i32> = None;
let emoticon: Option<i32> = None;

// The `if let` construct reads: "if `let` destructures `number` into
// `Some(i)`, evaluate the block (`{}`).
if let Some(i) = number {
println!("Matched {:?}!", i);
}

// If you need to specify a failure, use an else:
if let Some(i) = letter {
println!("Matched {:?}!", i);
} else {
// Destructure failed. Change to the failure case.
println!("Didn't match a number. Let's go with a letter!");
}

// Provide an altered failing condition.
let i_like_letters = false;

if let Some(i) = emoticon {
println!("Matched {:?}!", i);
// Destructure failed. Evaluate an `else if` condition to see if the
// alternate failure branch should be taken:
} else if i_like_letters {
println!("Didn't match a number. Let's go with a letter!");
} else {
// The condition evaluated false. This branch is the default:
println!("I don't like letters. Let's go with an emoticon :)!");
}
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");
}
```

In the same way, `if let` can be used to match any enum value:
if and if let expressions can be intermixed:
```rust,editable
let x = Some(3);
let a = if let Some(1) = x {
1
} else if x == Some(2) {
2
} else if let Some(y) = x {
y
} else {
-1
};
assert_eq!(a, 3);
```
An if let expression is equivalent to a match expression as follows:

```rust,editable
// Our example enum
enum Foo {
Bar,
Baz,
Qux(u32)
if let PATS = EXPR {
/* body */
} else {
/*else */
}
```
is equivalent to

fn main() {
// Create example variables
let a = Foo::Bar;
let b = Foo::Baz;
let c = Foo::Qux(100);

// Variable a matches Foo::Bar
if let Foo::Bar = a {
println!("a is foobar");
}

// Variable b does not match Foo::Bar
// So this will print nothing
if let Foo::Bar = b {
println!("b is foobar");
}

// Variable c matches Foo::Qux which has a value
// Similar to Some() in the previous example
if let Foo::Qux(value) = c {
println!("c is {}", value);
}

// Binding also works with `if let`
if let Foo::Qux(value @ 100) = c {
println!("c is one hundred");
}
```rust,editable
match EXPR {
PATS => { /* body */ },
_ => { /* else */ }, // () if there is no else
}
```
Multiple patterns may be specified with the | operator. This has the same semantics as with | in match expressions:

Another benefit is that `if let` allows us to match non-parameterized enum variants. This is true even in cases where the enum doesn't implement or derive `PartialEq`. In such cases `if Foo::Bar == a` would fail to compile, because instances of the enum cannot be equated, however `if let` will continue to work.
```rust,editable
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);
}
```
The expression cannot be a lazy boolean operator expression. Use of a lazy boolean operator is ambiguous with a planned feature change of the language (the implementation of if-let chains - see eRFC 2947). When lazy boolean operator expression is desired, this can be achieved by using parenthesis as below:

Would you like a challenge? Fix the following example to use `if let`:
```rust,editable
// Before...
if let PAT = EXPR && EXPR { .. }

```rust,editable,ignore,mdbook-runnable
// This enum purposely neither implements nor derives PartialEq.
// That is why comparing Foo::Bar == a fails below.
enum Foo {Bar}
// After...
if let PAT = ( EXPR && EXPR ) { .. }

fn main() {
let a = Foo::Bar;
// Before...
if let PAT = EXPR || EXPR { .. }

// Variable a matches Foo::Bar
if Foo::Bar == a {
// ^-- this causes a compile-time error. Use `if let` instead.
println!("a is foobar");
}
}
// After...
if let PAT = ( EXPR || EXPR ) { .. }
```


### See also:

[`enum`][enum], [`Option`][option], and the [RFC][if_let_rfc]
Expand Down
24 changes: 21 additions & 3 deletions src/flow_control/match.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,26 @@
# match

Rust provides pattern matching via the `match` keyword, which can be used like
a C `switch`. The first matching arm is evaluated and all possible values must be
covered.
A match expression branches on a pattern. The exact form of matching that occurs depends on the pattern. A match expression has a scrutinee expression, which is the value to compare to the patterns. The scrutinee expression and the patterns must have the same type.

A match behaves differently depending on whether or not the scrutinee expression is a place expression or value expression. If the scrutinee expression is a value expression, it is first evaluated into a temporary location, and the resulting value is sequentially compared to the patterns in the arms until a match is found. The first arm with a matching pattern is chosen as the branch target of the match, any variables bound by the pattern are assigned to local variables in the arm's block, and control enters the block.

When the scrutinee expression is a place expression, the match does not allocate a temporary location; however, a by-value binding may copy or move from the memory location. When possible, it is preferable to match on place expressions, as the lifetime of these matches inherits the lifetime of the place expression rather than being restricted to the inside of the match.

An example of a match expression:

```rust,editable
let x = 1;

match x {
1 => println!("one"),
2 => println!("two"),
3 => println!("three"),
4 => println!("four"),
5 => println!("five"),
_ => println!("something else"),
}
```
Another Example

```rust,editable
fn main() {
Expand Down