From 5174fd5202efaef6621f52d18bb7c5fa8e96192f Mon Sep 17 00:00:00 2001 From: Arpeet Makasare <87971268+Arpeet007@users.noreply.github.com> Date: Wed, 28 Sep 2022 14:14:43 +0530 Subject: [PATCH 1/5] Update match.md Added a Detailed Description of Match Expression With An Example Compared to The Previous Document --- src/flow_control/match.md | 44 +++++++++++++-------------------------- 1 file changed, 14 insertions(+), 30 deletions(-) diff --git a/src/flow_control/match.md b/src/flow_control/match.md index 6beeea3668..0672de0653 100644 --- a/src/flow_control/match.md +++ b/src/flow_control/match.md @@ -1,37 +1,21 @@ # 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. -```rust,editable -fn main() { - let number = 13; - // TODO ^ Try different values for `number` +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. - println!("Tell me about {}", number); - match number { - // Match a single value - 1 => println!("One!"), - // Match several values - 2 | 3 | 5 | 7 | 11 => println!("This is a prime"), - // TODO ^ Try adding 13 to the list of prime values - // Match an inclusive range - 13..=19 => println!("A teen"), - // Handle the rest of cases - _ => println!("Ain't special"), - // TODO ^ Try commenting out this catch-all arm - } +An example of a match expression: - let boolean = true; - // Match is an expression too - let binary = match boolean { - // The arms of a match must cover all the possible values - false => 0, - true => 1, - // TODO ^ Try commenting out one of these arms - }; +```rust,editable +let x = 1; - println!("{} -> {}", boolean, binary); +match x { + 1 => println!("one"), + 2 => println!("two"), + 3 => println!("three"), + 4 => println!("four"), + 5 => println!("five"), + _ => println!("something else"), } -``` From 3b01a62a405ddadb05026d9f73323429c088bc24 Mon Sep 17 00:00:00 2001 From: Arpeet Makasare <87971268+Arpeet007@users.noreply.github.com> Date: Wed, 28 Sep 2022 14:22:14 +0530 Subject: [PATCH 2/5] Update if_let.md Made Changes To the Explanations of if let expression and The examples explain what can and cannot be done and used in a simpler manner --- src/flow_control/if_let.md | 174 ++++++++++++++----------------------- 1 file changed, 66 insertions(+), 108 deletions(-) diff --git a/src/flow_control/if_let.md b/src/flow_control/if_let.md index 3742b6f683..7ec19d0ddb 100644 --- a/src/flow_control/if_let.md +++ b/src/flow_control/if_let.md @@ -1,123 +1,81 @@ -# if let - -For some use cases, when matching enums, `match` is awkward. For example: - -```rust -// Make `optional` of type `Option` -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 ) )? + +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. + +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"); +} -``` - -`if let` is cleaner for this use case and in addition allows various -failure options to be specified: - -```rust,editable -fn main() { - // All have type `Option` - let number = Some(7); - let letter: Option = None; - let emoticon: Option = 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 :)!"); - } +// this body will execute +if let ("Ham", b) = dish { + println!("Ham is served with {}", b); } -``` -In the same way, `if let` can be used to match any enum value: +if let _ = 5 { + println!("Irrefutable patterns are always true"); +} +if and if let expressions can be intermixed: + +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: + +if let PATS = EXPR { + /* body */ +} else { + /*else */ +} +is equivalent to -```rust,editable -// Our example enum -enum Foo { - Bar, - Baz, - Qux(u32) +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: -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"); - } +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: -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. +// Before... +if let PAT = EXPR && EXPR { .. } -Would you like a challenge? Fix the following example to use `if let`: +// After... +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} +// Before... +if let PAT = EXPR || EXPR { .. } -fn main() { - let a = Foo::Bar; +// After... +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"); - } -} -``` ### See also: From 275560b03f4717a49889d5d8e68188b670b64a76 Mon Sep 17 00:00:00 2001 From: Arpeet Makasare <87971268+Arpeet007@users.noreply.github.com> Date: Wed, 28 Sep 2022 14:27:38 +0530 Subject: [PATCH 3/5] Update if_let.md Changed Descripton of if let and added examples to demonstrate what can and cannot be done --- src/flow_control/if_let.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/flow_control/if_let.md b/src/flow_control/if_let.md index 7ec19d0ddb..c9ad57405e 100644 --- a/src/flow_control/if_let.md +++ b/src/flow_control/if_let.md @@ -7,6 +7,7 @@ IfLetExpression : 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 let dish = ("Ham", "Eggs"); // this body will be skipped because the pattern is refuted @@ -25,8 +26,9 @@ if let ("Ham", b) = dish { if let _ = 5 { println!("Irrefutable patterns are always true"); } -if and if let expressions can be intermixed: +if and if let expressions can be intermixed: +```rust,editable let x = Some(3); let a = if let Some(1) = x { 1 @@ -40,6 +42,7 @@ let a = if let Some(1) = x { assert_eq!(a, 3); An if let expression is equivalent to a match expression as follows: +```rust,editable if let PATS = EXPR { /* body */ } else { @@ -47,12 +50,14 @@ if let PATS = EXPR { } is equivalent to +```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: +```rust,editable enum E { X(u8), Y(u8), @@ -64,6 +69,7 @@ if let E::X(n) | E::Y(n) = v { } 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: +```rust,editable // Before... if let PAT = EXPR && EXPR { .. } From 5c17e07fe0220ef82bec212806f9351b1a5ecbc6 Mon Sep 17 00:00:00 2001 From: Arpeet Makasare <87971268+Arpeet007@users.noreply.github.com> Date: Wed, 28 Sep 2022 14:30:58 +0530 Subject: [PATCH 4/5] Update if_let.md --- src/flow_control/if_let.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/flow_control/if_let.md b/src/flow_control/if_let.md index c9ad57405e..19235fb178 100644 --- a/src/flow_control/if_let.md +++ b/src/flow_control/if_let.md @@ -26,6 +26,7 @@ if let ("Ham", b) = dish { if let _ = 5 { println!("Irrefutable patterns are always true"); } +``` if and if let expressions can be intermixed: ```rust,editable @@ -40,6 +41,7 @@ let a = if let Some(1) = x { -1 }; assert_eq!(a, 3); +``` An if let expression is equivalent to a match expression as follows: ```rust,editable @@ -48,6 +50,7 @@ if let PATS = EXPR { } else { /*else */ } +``` is equivalent to ```rust,editable @@ -67,6 +70,7 @@ 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: ```rust,editable @@ -81,6 +85,7 @@ if let PAT = EXPR || EXPR { .. } // After... if let PAT = ( EXPR || EXPR ) { .. } +``` ### See also: From 580c6c996f570f6a069a77f6d873525f830c0330 Mon Sep 17 00:00:00 2001 From: Arpeet Makasare <87971268+Arpeet007@users.noreply.github.com> Date: Fri, 7 Oct 2022 12:01:09 +0530 Subject: [PATCH 5/5] Update match.md --- src/flow_control/match.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/flow_control/match.md b/src/flow_control/match.md index 0672de0653..fbc2ce70d8 100644 --- a/src/flow_control/match.md +++ b/src/flow_control/match.md @@ -19,3 +19,37 @@ match x { 5 => println!("five"), _ => println!("something else"), } +``` +Another Example + +```rust,editable +fn main() { + let number = 13; + // TODO ^ Try different values for `number` + + println!("Tell me about {}", number); + match number { + // Match a single value + 1 => println!("One!"), + // Match several values + 2 | 3 | 5 | 7 | 11 => println!("This is a prime"), + // TODO ^ Try adding 13 to the list of prime values + // Match an inclusive range + 13..=19 => println!("A teen"), + // Handle the rest of cases + _ => println!("Ain't special"), + // TODO ^ Try commenting out this catch-all arm + } + + let boolean = true; + // Match is an expression too + let binary = match boolean { + // The arms of a match must cover all the possible values + false => 0, + true => 1, + // TODO ^ Try commenting out one of these arms + }; + + println!("{} -> {}", boolean, binary); +} +```