|
| 1 | +- Feature Name: Alternatives in patterns |
| 2 | +- Start Date: 2016-02-14 |
| 3 | +- RFC PR: (leave this empty) |
| 4 | +- Rust Issue: (leave this empty) |
| 5 | + |
| 6 | +# Summary |
| 7 | +[summary]: #summary |
| 8 | +Extend the pattern syntax for alternatives in `match` statement, allow alternatives for pattern matching in `let` and `if let` statements. |
| 9 | + |
| 10 | +# Motivation |
| 11 | +[motivation]: #motivation |
| 12 | + |
| 13 | +Rust allows alternatives ( `|` ) in pattern syntax for `match`, but only for 'top-level' of pattern. |
| 14 | +This aims to reduce verbosity in certain examples and increase expressiveness. |
| 15 | + |
| 16 | +Also, this RFC proposes to allow alternatives in `let` or `if let` statements. |
| 17 | + |
| 18 | +# Detailed design |
| 19 | +[design]: #detailed-design |
| 20 | + |
| 21 | +## Expand usage of alternatives to 'deeper levels' |
| 22 | +Imagine a type: |
| 23 | +```rust |
| 24 | +struct NewType (Result<String,String>, u8); |
| 25 | +``` |
| 26 | + |
| 27 | +Exhaustive `match` statement for this type would look like this: |
| 28 | +```rust |
| 29 | +match new_type { |
| 30 | + NewType(Ok(e), num) | NewType(Err(e), num) => println!("ok with {}: {}", num, e) |
| 31 | +} |
| 32 | +``` |
| 33 | + |
| 34 | +This RFC proposes a following replacement: |
| 35 | +```rust |
| 36 | +match new_type { |
| 37 | + NewType(Ok(e) | Err(e), num) => println!("ok with {}: {}", num, e) |
| 38 | +} |
| 39 | +``` |
| 40 | + |
| 41 | + |
| 42 | +Little bit more complicated example: |
| 43 | +```rust |
| 44 | +#[derive(Clone, Copy)] enum Test { First, Second } |
| 45 | +//Current Rust: |
| 46 | +match (test1, test2) { |
| 47 | + (First, First) | (First, Second) | (Second, First) | (Second, Second) => println!("matches") |
| 48 | +} |
| 49 | +//This RFC proposes: |
| 50 | + |
| 51 | +match (test1, test2) { |
| 52 | + (First| Second, First | Second) => println!("matches") |
| 53 | +} |
| 54 | +``` |
| 55 | + |
| 56 | +## Allow alternatives in `let` statements |
| 57 | +Currently expressions like `Ok(e) | Err(e)` are not allowed in `let` or `if let` statements, which brings inconsistence to pattern matching. |
| 58 | +This RFC proposes following to be allowed: |
| 59 | +```rust |
| 60 | +if let (First | Second) = three_variants_enum {} |
| 61 | +let (Ok(e) | Err(e)) = result; |
| 62 | +let closure = |(Ok(e) | Err(e))| println!(e); // works similar to the statement above |
| 63 | +``` |
| 64 | + |
| 65 | +### Parentheses around patterns in `let` statements |
| 66 | +Multiple alternatives should be enclosed in parentheses and represent a single pattern, |
| 67 | +while single alternative should not be enclosed with parens to be backwards compatible. |
| 68 | +```rust |
| 69 | +enum Three{ A(i32), B(i32), C(i32) } |
| 70 | +if let A(i) = three {} |
| 71 | +if let (A(i) | B(i)) = three {} |
| 72 | +``` |
| 73 | +Parens should be introduced due to: |
| 74 | +- Pattern matching in closure arguments: |
| 75 | + |
| 76 | +```rust |
| 77 | +let closure = | Ok(i) | Err(i) | i; // Is it possible to find pattern's end and actual closure' start? |
| 78 | + |
| 79 | +let closure = |(Ok(i) | Err(i))| i; // As proposed by this RFC |
| 80 | +``` |
| 81 | +- Follow rules for macros (as discussed in [1384#comment](https://github.com/rust-lang/rfcs/pull/1384#issuecomment-164275799)) |
| 82 | + |
| 83 | +### Irrefutable patterns |
| 84 | +Patterns in `let` statements must be irrefutable - meaning they must cover every possible variant: |
| 85 | +```rust |
| 86 | +enum Three{ First(u8), Second(u8), Third(u8) } |
| 87 | +//... |
| 88 | +let First(u) | Second(u) = three; //Not allowed! |
| 89 | +``` |
| 90 | + |
| 91 | +Patterns in `if let` statements should be disallowed to be irrefutable, it means they are not allowed to cover every possible variant: |
| 92 | +```rust |
| 93 | +if let Ok(e) | Err(e) = result { |
| 94 | + //Not allowed! |
| 95 | +} else {} |
| 96 | +``` |
| 97 | +If pattern is irrefutable, then an `else`-branch will never be executed, and `if` will be redundant. |
| 98 | + |
| 99 | +# Drawbacks |
| 100 | +[drawbacks]: #drawbacks |
| 101 | + |
| 102 | +These features, probably, are not easy to implement. |
| 103 | + |
| 104 | +# Alternatives |
| 105 | +[alternatives]: #alternatives |
| 106 | + |
| 107 | +- **This is a subset of [#99](https://github.com/rust-lang/rfcs/pull/99).** The original RFC was postponed and as suggested by [#1456](https://github.com/rust-lang/rfcs/issues/1456#issuecomment-173943563) a new RFC was created with a link to postponed one. |
| 108 | +- **Implement the proposal only for `match`.** This has a downside of further increased inconsistence. |
| 109 | +- **Allow irrefutable patterns in `if let` statements.** This way, `else`-branch will not always execute. If so, a warning about unreachable code should be emitted. |
| 110 | + |
| 111 | +# Unresolved questions |
| 112 | +[unresolved]: #unresolved-questions |
| 113 | + |
| 114 | +- The possibility of treating single variant w/o parens as a pattern (as it is treated today) simultaneously with treating multiple variants with parens as a pattern. |
| 115 | +- The requirement of parens around multiple alternatives in *deeper levels* of pattern matching, i.e is this legal: |
| 116 | +```rust |
| 117 | +match new_type { |
| 118 | + NewType(Ok(e) | Err(e), num) => println!("ok with {}: {}", num, e) |
| 119 | +} |
| 120 | +``` |
0 commit comments