# Idea: PlacePattern

**URL:** <https://internals.rust-lang.org/t/idea-placepattern/11090>\
**Category:** language design\
**Created:** [October 12, 2019, 6:39am UTC](https://internals.rust-lang.org/t/idea-placepattern/11090 "2019-10-12T06:39:28Z")\
**Posts on this page:** 13\
**Page:** 2

<div class="post-metadata">

**Author:** ![Aloso](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/aloso/32/5039_2.png) [@Aloso](https://internals.rust-lang.org/u/Aloso)\
**Post date:** [October 14, 2019, 11:55pm UTC](https://internals.rust-lang.org/t/idea-placepattern/11090/21 "2019-10-14T23:55:04Z")

</div>

Then we would have two syntaxes for the same purpose: `let (foo, bar)` and `(let foo, let bar)`. Some people might find this confusing (especially people who are just learning the language). We could deprecate `let (foo, bar)` in a new edition, but I believe many Rustaceans wouldn't like that.

---

<div class="post-metadata">

**Author:** ![josh](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/josh/32/5934_2.png) [@josh](https://internals.rust-lang.org/u/josh)\
**Post date:** [October 15, 2019, 7:32am UTC](https://internals.rust-lang.org/t/idea-placepattern/11090/22 "2019-10-15T07:32:41Z")

</div>

So, it sounds like there are two separate ideas here, both of which seem worth considering and potentially RFCing.

1. The `in` pattern: anywhere you could put an identifier for a fresh variable name, you can instead put `in` followed by an existing path to store the matched value into that existing path as though via an assignment. You can put multiple `in` matches and non-`in` matches in the same expression, and they'll get assigned or created (respectively) left-to-right. You can use `in` with `|` patterns, and only the `in` matches in the matching branch of the `|` will take effect. This works in any pattern-matching context (though it requires parentheses for disambiguation in a for loop, `for (in x) in ...`).

2. @comex's suggestion to allow simple aggregate assignment of tuples, or potentially other irrefutable patterns. Note that we need to carefully define this if we want `(x, y) = (y, x)` to do the right thing. What about `(x, x.a) = (m, n)`; what behavior should that have? Can we define semantics that do the right thing for both of those? I _think_ the rule we want is that the _entire_ right-hand side is evaluated first, then all the assignments take place. (I think we want the same rule for `let` and `in` patterns in (1) above.)

With my language team hat on (but not speaking for the rest of the language team): I'd love to see RFCs for both of these. I can imagine accepting one or accepting both, as they have somewhat different use cases. (1) works well in general pattern matching; (2) works well for simple assignment of multiple values.

---

<div class="post-metadata">

**Author:** ![Centril](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/centril/32/3334_2.png) [@Centril](https://internals.rust-lang.org/u/Centril)\
**Post date:** [October 15, 2019, 9:30am UTC](https://internals.rust-lang.org/t/idea-placepattern/11090/23 "2019-10-15T09:30:43Z")

</div>

> [@josh](#):
>
> (though it requires parentheses for disambiguation in a for loop, `for (in x) in ...` ).

It doesn't require parenthesis. I tried this out in libsyntax ([`parse_pat_with_range_pat`](https://doc.rust-lang.org/nightly/nightly-rustc/syntax/parse/parser/struct.Parser.html#method.parse_pat_with_range_pat))

```diff
             _ => if self.eat_keyword(kw::Underscore) {
                 // Parse _
                 PatKind::Wild
+ } else if self.eat_keyword(kw::In) {
+ let _ = self.parse_ident()?; // TODO; allow `p.f` as well
+ PatKind::Wild
             } else if self.eat_keyword(kw::Mut) {

```

and both `let (in x, in y) = (0, 1)` and `for in x in 0..1 {}` parse completely fine. I also don't think such a special case should be added to the `for` loop grammar because it would open up another entry point into the pattern grammar and further unnecessarily complicate it.

> [@josh](#):
>
> (I think we want the same rule for `let` and `in` patterns in (1) above.)

That brings up the case `if let (A(in x), B(_)) = expr { ... }`. It seems to me that no mutation should happen if the whole pattern doesn't match so that there's some atomicity about assignments in pattern matching. Otherwise, patterns themselves become rather side-effectful (tho this might be a good reason to go with @comex's idea instead and it would allow more general place expressions). I think @comex's idea should be grammatically just `$expr = $expr;` -- that is, we don't change the syntax of assignment at all, but rather just change the lowering to allow `($e_0, ..., $e_n)` to desugar into more assignments (although now one needs to take into account `[$e_0, ..., $e_n]` and friends as well as `_`).

> [@josh](#):
>
> With my language team hat on (but not speaking for the rest of the language team): I'd love to see RFCs for both of these.

But please not now. 🙂 we have enough ongoing implementation work on control flow and pattern matching as-is.

---

<div class="post-metadata">

**Author:** ![Aloso](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/aloso/32/5039_2.png) [@Aloso](https://internals.rust-lang.org/u/Aloso)\
**Post date:** [October 15, 2019, 10:01am UTC](https://internals.rust-lang.org/t/idea-placepattern/11090/24 "2019-10-15T10:01:11Z")

</div>

If destructuring assignment didn't require `let`, would this also apply to `if let` and `while let`? E.g.

```rust
if Ok(let foo) = bar {}
if "foo" = bar {}
if foo = bar {} // assigns bar to foo; likely a bug!

```

---

<div class="post-metadata">

**Author:** ![josh](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/josh/32/5934_2.png) [@josh](https://internals.rust-lang.org/u/josh)\
**Post date:** [October 15, 2019, 11:38am UTC](https://internals.rust-lang.org/t/idea-placepattern/11090/25 "2019-10-15T11:38:10Z")

</div>

> [@Centril](#):
>
> It doesn't require parenthesis.

The compiler can parse it unambiguously, but allowing it without parentheses would make parse errors more confusing, in my opinion. I think it'd be worth requiring the parenthesis.

> [@Centril](#):
>
> no mutation should happen if the whole pattern doesn't match

Agreed.

> [@Aloso](#):
>
> If destructuring assignment didn't require `let` , would this also apply to `if let` and `while let` ? E.g.
> 
> ```rust
> if Ok(let foo) = bar {}
> if "foo" = bar {}
> if foo = bar {} // assigns bar to foo; likely a bug!
> 
> ```

No, destructuring assignment would still evaluate to `()` so it wouldn't work in an `if` or other expression.

---

<div class="post-metadata">

**Author:** ![Centril](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/centril/32/3334_2.png) [@Centril](https://internals.rust-lang.org/u/Centril)\
**Post date:** [October 15, 2019, 12:15pm UTC](https://internals.rust-lang.org/t/idea-placepattern/11090/26 "2019-10-15T12:15:12Z")

</div>

> [@josh](#):
>
> The compiler can parse it unambiguously, but allowing it without parentheses would make parse errors more confusing, in my opinion. I think it'd be worth requiring the parenthesis.

Do you mean compiler errors (which ones, specifically?) or difficulties for humans reading it?

The parser recovery e.g. for `for ( x in y ) { ... }` introduced in [Syntax: Recover on `for ( $pat in $expr ) $block` by Centril · Pull Request #62928 · rust-lang/rust · GitHub](https://github.com/rust-lang/rust/pull/62928) took a minute to fix, and I don't imagine it would be substantially more difficult to fix other ones and provide targeted diagnostics where necessary.

As for humans reading it, I think if people think parsing `for in x in iter { ... }` is difficult then most people will not write it that way and include parenthesis (we can tweak the lint to not trigger in this case). Also, `for in x in iter { ... }` seems like an uncommon thing to do (just as `let in x = 0;` would be uncommon, because you can just use normal assignment instead...) so I think the requirement wouldn't protect users from a common pitfall (and then there's the type system protection as well). It does however substantially complicate the code in `parser/pat.rs` (and the grammar it is based on) because it is inconsistently requiring parenthesis just for `for`-loops but not in other places. All in all, I disagree that the requirement here is worth it.

---

<div class="post-metadata">

**Author:** ![lordan](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/lordan/32/2107_2.png) [@lordan](https://internals.rust-lang.org/u/lordan)\
**Post date:** [October 15, 2019, 3:26pm UTC](https://internals.rust-lang.org/t/idea-placepattern/11090/27 "2019-10-15T15:26:03Z")

</div>

TBH, I find the overloading of `in` confusing here, esp. within `for` loops.

@comex's suggestions are IMHO more elegant, but if patterns in pure assignments are not feasible, how about a hybrid approach where we use `mut` as introductory keyword for such pattern assigments (as suggested elsewhere), and optionally `let` within such `mut` assignments to introduce new variables:

```rust
mut (a, let b) = foo()
if mut x = foo() { ... }
if mut (A(x), B(let y)) = foo() { ... }
for mut x in iter { ... }

```

---

<div class="post-metadata">

**Author:** ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)\
**Post date:** [October 15, 2019, 3:42pm UTC](https://internals.rust-lang.org/t/idea-placepattern/11090/28 "2019-10-15T15:42:02Z")

</div>

That doesn't work because `mut` already has meaning in patterns.

[https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=3f3dad4ed64f743252333cb1fc394e55](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=3f3dad4ed64f743252333cb1fc394e55)

---

<div class="post-metadata">

**Author:** ![josh](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/josh/32/5934_2.png) [@josh](https://internals.rust-lang.org/u/josh)\
**Post date:** [October 15, 2019, 4:52pm UTC](https://internals.rust-lang.org/t/idea-placepattern/11090/29 "2019-10-15T16:52:45Z")

</div>

> [@Centril](#):
>
> Do you mean compiler errors (which ones, specifically?) or difficulties for humans reading it?

Both. The compiler can't as easily know what you meant to give a clear compiler error, and the human can't parse it as easily.

---

<div class="post-metadata">

**Author:** ![Centril](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/centril/32/3334_2.png) [@Centril](https://internals.rust-lang.org/u/Centril)\
**Post date:** [October 15, 2019, 5:01pm UTC](https://internals.rust-lang.org/t/idea-placepattern/11090/30 "2019-10-15T17:01:47Z")

</div>

> [@josh](#):
>
> The compiler can't as easily know what you meant to give a clear compiler error

_Maybe -- Let's be specific tho._ Which compiler error (e.g. in the parser) or one that you would like to see would become more difficult to encode? As I noted above, I already fixed one diagnostic in the libsyntax code (was trivial to do so) which related to `for`-loops.

> [@josh](#):
>
> and the human can't parse it as easily.

With any grammar there will always be corner cases more difficult to parse but they are allowed nonetheless because it would be hacky not to allow them and because these corner cases are uncommon (see aforementioned rationale, if you disagree with it I'd like to understand why).

---

<div class="post-metadata">

**Author:** ![josh](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/josh/32/5934_2.png) [@josh](https://internals.rust-lang.org/u/josh)\
**Post date:** [October 15, 2019, 6:24pm UTC](https://internals.rust-lang.org/t/idea-placepattern/11090/31 "2019-10-15T18:24:19Z")

</div>

> [@Centril](#):
>
> > The compiler can't as easily know what you meant to give a clear compiler error
> 
> _Maybe -- Let's be specific tho._ Which compiler error (e.g. in the parser) or one that you would like to see would become more difficult to encode?

> **[Rust Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=424648633452568ef067e86967c56974)**
>
> A browser interface to the Rust compiler to experiment with the language

```rust
error: expected pattern, found keyword `in`
 --> src/main.rs:2:9
  |
2 | for in vec![1, 2, 3] {
  | ^^ expected pattern

```

> [@Centril](#):
>
> they are allowed nonetheless because it would be hacky not to allow them and because these corner cases are uncommon

Assuming that we don't make error messages like the above any worse or more ambiguous, I don't have a problem with allowing `for in x in y` in the grammar and then steering people away from it and towards `for (in x) in y` with lints.

---

<div class="post-metadata">

**Author:** ![Centril](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/centril/32/3334_2.png) [@Centril](https://internals.rust-lang.org/u/Centril)\
**Post date:** [October 15, 2019, 7:11pm UTC](https://internals.rust-lang.org/t/idea-placepattern/11090/32 "2019-10-15T19:11:25Z")

</div>

> [@josh](#):
>
> [Rust Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=424648633452568ef067e86967c56974)

That's a pretty bad diagnostic actually, as it is fatal and not performing any recovery. _We can do better_ by parsing `for $pat` and then seeing that there is a missing `in` after the `vec![...]` (in fact, [`parse_for_expr`](https://doc.rust-lang.org/nightly/nightly-rustc/syntax/parse/parser/struct.Parser.html#method.parse_for_expr) already has such logic). We can make the diagnostic even better by looking at the `pat: P<Pat>` behind us and see if is `in $place` and then suggesting that the `in` be moved to after the `$place` together with a suggested pattern to match on. (Of course, we have to see whether this is a common pitfall that deserves a special cased diagnostic but I'm open to implementing it myself if users need it.)

---

<div class="post-metadata">

**Author:** ![system](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/system/32/14092_2.png) [@system](https://internals.rust-lang.org/u/system)\
**Post date:** [January 13, 2020, 7:11pm UTC](https://internals.rust-lang.org/t/idea-placepattern/11090/33 "2020-01-13T19:11:40Z")

</div>

Closed on January 13, 2020, 7:11pm UTC

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.

[Previous page](https://internals.rust-lang.org/t/idea-placepattern/11090.md?page=1)
