# Pre-RFC: View Patterns

**URL:** <https://internals.rust-lang.org/t/pre-rfc-view-patterns/9628>\
**Category:** language design\
**Created:** [March 16, 2019, 8:25pm UTC](https://internals.rust-lang.org/t/pre-rfc-view-patterns/9628 "2019-03-16T20:25:11Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![arthrowpod](https://avatars.discourse-cdn.com/v4/letter/a/df788c/32.png) [@arthrowpod](https://internals.rust-lang.org/u/arthrowpod)\
**Post date:** [March 16, 2019, 8:25pm UTC](https://internals.rust-lang.org/t/pre-rfc-view-patterns/9628/1 "2019-03-16T20:25:12Z")

</div>

**TL;DR** : Introduce patterns of the form `f -> pattern`, which have the semantics of applying `f` to the matched value and then matching `pattern` on the result. This feature generalizes pattern synonyms, `box_patterns`, `advanced_slice_patterns`, etc.

* * *

Pattern matching is a very powerful construct. It makes it very easy to work with complex structures like nested slices, lists, and ASTs.

However, pattern matching in its current form still has many limitations. For example, there is no stable way to match the contents of `Box` or an `Rc`. There is no way to abstract common patterns, short of using a macro (which has its own limitations; for example, macro-generated patterns defined in a module `foo` cannot match on private types from `foo` – [example](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=cc967f0eeb90b5033a497d95ca81ee2e)).

It would also be nice if libraries could provide pattern-matching constructs other than what are built in via enums. For example, one might want a way to match a slice that starts with a certain prefix and then bind a variable to the sub-slice that comes after that prefix. There is currently no way to do this.

In short: **patterns currently can’t build abstractions**.

What I propose is that we add a feature analogous to Haskell’s [view patterns](https://ghc.haskell.org/trac/ghc/wiki/ViewPatterns#Basicviewpatterns) (or ‘the one pattern to rule them all’), with a few modifications to account for the specialties of Rust’s borrow checker.

## Example

As an example of where this might be useful, consider the following code:

```rust
fn to_digit(c: char) -> Option<u32> {
    c.to_digit(10)
}
fn classify(c: char) -> CharClass {
    match c {
        ' ' | '\t' | '\n' => CharClass::Whitespace,
        c where c.is_ascii_alphabetic() => CharClass::Alpha,
        c where to_digit(c).is_some()
            => CharClass::Digit(to_digit(c).unwrap()),
        _ => CharClass::Other,
    }
}

```

The third arm – the one that checks for digits – is quite ugly. With view patterns, it could be rewritten as:

```rust
/* ... */
fn classify(c: char) -> CharClass {
    match c {
        ' ' | '\t' | '\n' => CharClass::Whitespace,
        c where c.is_ascii_alphabetic() => CharClass::Alpha,
        (to_digit -> Some(n)) => CharClass::Digit(n),
        _ => CharClass::Other,
    }
}

```

## Interaction With Ownership

Rust distinguishes between patterns that move the value they match on and patterns that only borrow it. This is an important distinction because a construct like `a @ subpattern` can only work if `subpattern` doesn’t move the value it matches on.

Because of this distinction, we will need three different forms of view pattern: one where the view function takes its argument by value, on where it takes it by reference, and one by mutable reference. For this example, I’ll be using `f -> p`, `ref f -> p`, and `ref mut f -> p` respectively, but this is open to change (in in fact probably should be).

To be precise:

- `if let (f -> pat) = v {}` is equivalent to `if let pat = f(v) {}`.
- `if let (ref f -> pat) = v {}` is equivalent to `if let pat = f(&v) {}`.
- `if let (ref mut f -> pat) = v {}` is equivalent to `if let pat = f(&mut v) {}`.

## Exhaustiveness

A view pattern such as `f -> pat` should be considered irrefutable if and only if `pat` is irrefutable. Otherwise, no assumptions should be made about whether this will match.

## Pros

- This allows one to express some constructs that cannot currently be written. The example code shown above cannot currently be written with a `match` without a redundant `unwrap` because `if let` guards are not supported and there’s no way to fallthrough to the next arm of a `match`.

- We can continue adding new pattern constructs such as range patterns, box patterns, advanced slice patterns, etc., but this feature subsumes all of them and also allows library authors to define their own.

## Cons

- This adds more complexity to patterns.
- The feature is potentially confusing and hard to search for.

There are probably more problems with this that I haven’t thought of.

## Bikeshedding

- Should the parentheses always be required around view patterns?
- I believe the current syntax would cause infinite lookahead, so we probably want some kind of mandatory prefix.
- It would be nice if there was a way to write a method call in place of `f`, or to otherwise include functions that don’t just take one parameter. You can use a lambda, but `(|c| c.to_digit(10)) -> Some(n)` looks a bit ugly.
- It might be more ergonomic to have the difference between `(f -> pat)`, `(ref f -> pat)`, and `(ref mut f -> pat)` be inferred instead of explicit.
- Should we have special syntax for `f -> Some(p)` and `f -> true`?

---

<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:** [March 16, 2019, 11:11pm UTC](https://internals.rust-lang.org/t/pre-rfc-view-patterns/9628/2 "2019-03-16T23:11:26Z")

</div>

View patterns are quite a nice thing in Haskell but I think they pose challenges to overcome when adapting them for Rust. I think perhaps we've accepted some changes to pattern matching and control flow lately and giving it a breather before doing more changes would be good given our probable roadmap goals for 2019. I think also introducing pattern synonyms might be a step to take before this one. That said, here are some questions and notes for you to ponder.

> [@arthrowpod](#):
>
> This feature generalizes pattern synonyms

[`-XPatternFamilies`](https://ghc.haskell.org/trac/ghc/wiki/PatternFamilies) do, `-XViewPatterns` don't.

> [@arthrowpod](#):
>
> A view pattern such as `f -> pat` should be considered irrefutable if and only if `pat` is irrefutable. Otherwise, no assumptions should be made about whether this will match.

How do you get things to compose with move semantics and how do you get this to be efficient? E.g. consider:

```rust
enum E { A, B, C }
enum F { A, B }

fn foo(x: usize) -> Option<E> { ... }
fn bar(x: E) -> Option<F> { ... }

match foo(x) {
    Some(bar -> Some(F::A)) => a,
    Some(bar -> Some(F::B)) => b,
    _ => c,
}

```

Ideally, we want something like:

```rust
'_0: {
    match foo(x) {
        Some(_1) => match bar(_1) {
            Some(F::A) => break '_0 a,
            Some(F::B) => break '_0 b,
            _ => {},
        },
        _ => {},
    }

    c
}

```

but side-effects and poor desugarings could instead apply `bar(x)` twice.

I think it's also important that you consider how `pat ::= ... | expr -> pat ;` can nest arbitrarily as a pattern form.

> [@arthrowpod](#):
>
> because `if let` guards are not supported and there’s no way to fallthrough to the next arm of a `match` .

See [Tracking issue for RFC 2294, "if let guard" · Issue #51114 · rust-lang/rust · GitHub](https://github.com/rust-lang/rust/issues/51114).

> [@arthrowpod](#):
>
> I believe the current syntax would cause infinite lookahead, so we probably want some kind of mandatory prefix.

Can you say why? (I'm sure it's true I just haven't thought about it).

> [@arthrowpod](#):
>
> It might be more ergonomic to have the difference between `(f -> pat)` , `(ref f -> pat)` , and `(ref mut f -> pat)` be inferred instead of explicit.

Sounds nice, how is this achieved?

> [@arthrowpod](#):
>
> - Should we have special syntax for `f -> Some(p)` and `f -> true` ?

Doesn't seem warranted. We don't have it in `if let`.

---

<div class="post-metadata">

**Author:** ![arthrowpod](https://avatars.discourse-cdn.com/v4/letter/a/df788c/32.png) [@arthrowpod](https://internals.rust-lang.org/u/arthrowpod)\
**Post date:** [March 17, 2019, 12:28am UTC](https://internals.rust-lang.org/t/pre-rfc-view-patterns/9628/3 "2019-03-17T00:28:32Z")

</div>

The points you make are probably a good argument for not adding (or at least delaying decision on) this feature.

> [`-XPatternFamilies`](https://ghc.haskell.org/trac/ghc/wiki/PatternFamilies) do, `-XViewPatterns` don’t.

Everything that can be done with pattern synonyms can be done with view patterns. For example, the view function

```rust

fn join<T, E1, E2>(r: Result<Result<T, E1>, E2>) -> Option<T> {
    match r {
        Ok(Ok(t)) => Some(t),
        _ => None,
    }
}

```

acts like the unidirectional pattern synonym `pattern Join r = Right (Right r)`.

> [@Centril](#):
>
> See [Tracking issue for RFC 2294, "if let guard" · Issue #51114 · rust-lang/rust · GitHub](https://github.com/rust-lang/rust/issues/51114).

I didn't know about that. Given that if-let guards and view patterns are equally powerful I'd probably be fine with either one.

Regarding infinite lookahead: if it's ambiguous whether `f` is a pattern or an expression then the parser wouldn't know which one it is until it encounters the `->`.

> [@Centril](#):
>
> > It might be more ergonomic to have the difference between `(f -> pat)` , `(ref f -> pat)` , and `(ref mut f -> pat)` be inferred instead of explicit.
> 
> Sounds nice, how is this achieved?

I don't know, but it seems like something that needs to be solved anyway if we intend for something like `b @ box (ref n)` to ever be valid (although it's quite possible that we don't).

---

<div class="post-metadata">

**Author:** ![mcy](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mcy/32/6512_2.png) [@mcy](https://internals.rust-lang.org/u/mcy)\
**Post date:** [March 17, 2019, 2:58pm UTC](https://internals.rust-lang.org/t/pre-rfc-view-patterns/9628/4 "2019-03-17T14:58:19Z")

</div>

> [@arthrowpod](#):
>
> Regarding infinite lookahead: if it’s ambiguous whether `f` is a pattern or an expression then the parser wouldn’t know which one it is until it encounters the `->` .

- `|x| expr -> pat`: is it `(|x| expr) -> pat` or `| (x) | (expr -> pat)`?

- `|x| -> T expr -> pat`, is, hilariously, _not_ ambiguous, afaict.

---

<div class="post-metadata">

**Author:** ![H2CO3](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/h2co3/32/2849_2.png) [@H2CO3](https://internals.rust-lang.org/u/H2CO3)\
**Post date:** [March 18, 2019, 7:30am UTC](https://internals.rust-lang.org/t/pre-rfc-view-patterns/9628/6 "2019-03-18T07:30:30Z")

</div>

> [@arthrowpod](#):
>
> which have the semantics of applying `f` to the matched value and then matching `pattern` on the result

I have to ask, why? Why can't you just call the function with the value and pattern match on the return value? This seems redundant.

> [@arthrowpod](#):
>
> For example, there is no stable way to match the contents of `Box` or an `Rc`

How so? Both types provide access to the underlying value (e.g. `Deref`); they would be pretty useless otherwise.

> [@arthrowpod](#):
>
> It would also be nice if libraries could provide pattern-matching constructs

I have to disagree here. I would find code with custom pattern matching rules hard to read; I don't want arbitrary code to be disguised as a common language feature.

> [@arthrowpod](#):
>
> **patterns currently can’t build abstractions**.

This is debatable at best; patterns are already an abstraction, but I don't think arguing semantics is particularly productive. Patterns are suited for one thing, they do it well, and I don't think we should hang more and more bells and whistles on them, they are already quite complex.

---

<div class="post-metadata">

**Author:** ![comex](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/comex/32/2587_2.png) [@comex](https://internals.rust-lang.org/u/comex)\
**Post date:** [March 18, 2019, 9:10am UTC](https://internals.rust-lang.org/t/pre-rfc-view-patterns/9628/7 "2019-03-18T09:10:52Z")

</div>

> [@H2CO3](#):
>
> This is debatable at best; patterns are already an abstraction, but I don’t think arguing semantics is particularly productive. Patterns are suited for one thing, they do it well, and I don’t think we should hang more and more bells and whistles on them, they are already quite complex.

I disagree with this point. Among other things, patterns are hard to use with types that are semantically something like an enum but use a custom layout for higher efficiency. You can have a method to convert to a regular enum and then match on that, but for that to actually produce efficient code is a big ask for the optimizer. In lieu of that, if you want efficient layout, you have to sacrifice ergonomics, which is ideally something Rust would avoid.

I'm not sure how well this particular proposal would solve that, but extensible patterns in general are a feature I'm quite interested in.

---

<div class="post-metadata">

**Author:** ![arthrowpod](https://avatars.discourse-cdn.com/v4/letter/a/df788c/32.png) [@arthrowpod](https://internals.rust-lang.org/u/arthrowpod)\
**Post date:** [March 19, 2019, 3:56am UTC](https://internals.rust-lang.org/t/pre-rfc-view-patterns/9628/8 "2019-03-19T03:56:21Z")

</div>

> [@H2CO3](#):
>
> I have to ask, why? Why can’t you just call the function with the value and pattern match on the return value? This seems redundant.

You can't do this when nested inside another pattern. The code in the original post provides an example of a place where our current tools are very awkward.

> [@H2CO3](#):
>
> [`Rc` and `Box`] provide access to the underlying value (e.g. `Deref` ); they would be pretty useless otherwise.

Again, `deref` cannot be called when the value in question is nested inside of another pattern.

> [@H2CO3](#):
>
> I have to disagree here. I would find code with custom pattern matching rules hard to read; I don’t want arbitrary code to be disguised as a common language feature.

This is subjective, but I can see where you're coming from. The name of the function is there, but it can still be unclear. That said, Haskell code using view patterns tends to use them fairly sparingly and I haven't seen a place where it made things particularly unclear.

---

<div class="post-metadata">

**Author:** ![H2CO3](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/h2co3/32/2849_2.png) [@H2CO3](https://internals.rust-lang.org/u/H2CO3)\
**Post date:** [March 19, 2019, 7:25am UTC](https://internals.rust-lang.org/t/pre-rfc-view-patterns/9628/9 "2019-03-19T07:25:14Z")

</div>

> [@arthrowpod](#):
>
> The code in the original post provides an example

> [@arthrowpod](#):
>
> I haven’t seen a place where it made things particularly unclear.

In that example, even though it's a simple one, it _is_ quite confusing. Furthermore, `.is_some()` followed by `unwrap()` is an oft-encountered pattern which is trivially rewritten using `if let` or an inner `match`:

```rust
match c {
    ' ' | '\t' | '\n' => CharClass::Whitespace,
    c where c.is_ascii_alphabetic() => CharClass::Alpha,
    c => if let Some(digit) = to_digit(c) {
        CharClass::Digit(digit)
    } else {
        CharClass::Other
    }
}

```

This is perfectly clear, as short as it reasonably needs to be, and doesn't require any additional language features. I would say that there is no reason why you should have written the code in your first example in the first place, nor does this example illustrate well how the proposal is more useful than complicated.

* * *

To be honest, everytime someone tries to justify a language feature by arguing that it makes some convoluted code clearer, I invariably think that the code in question just needs a bit more thought and effort to write in a more considerate and systematic manner, using the already wide palette of existing elements of Rust.

It's possible to come up with virtually any number of (code snippet, feature) pairs where the former would be made "shorter" or "clearer" (for some, often subjective definition of these words) by the latter. But we shouldn't be so preoccupied with whether we _can_ that we don't ask whether we _should_. In particular, I don't think it's healthy to continuously (and additively) adapt the language to individuals' taste, it is simply not a sustainable design and development model. Instead, users should become experienced in using existing features, learn idioms, common patterns of refactoring (and antipatterns to be refactored), and adapt their own way of thinking to the language, if they want to deal in the language.

---

<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:** [March 19, 2019, 10:44am UTC](https://internals.rust-lang.org/t/pre-rfc-view-patterns/9628/10 "2019-03-19T10:44:34Z")

</div>

> [@H2CO3](#):
>
> Furthermore, `.is_some()` followed by `unwrap()` is an oft-encountered pattern which is trivially rewritten using `if let` or an inner `match` :

Simpler:

```rust
match c {
    ' ' | '\t' | '\n' => CharClass::Whitespace,
    c if c.is_ascii_alphabetic() => CharClass::Alpha,
    c if let Some(digit) = to_digit(c) => CharClass::Digit(digit),
    _ => CharClass::Other,
}

```

What is nice about this is that you have less rightward drift, which is generally something you should fight for more readable code. One could easily imagine that the bodies of each arm is quite indented. Shaving off one level of indent makes it less likely that you go over the 80/100 limit and so that often makes code cleaner.

> [@H2CO3](#):
>
> To be honest, everytime someone tries to justify a language feature by arguing that it makes some convoluted code clearer

Why do you assume their code is convoluted? This extension exists in Haskell and is useful in real world code. There were occasions today when hacking on rustc where this would have been useful.

> [@H2CO3](#):
>
> But we shouldn’t be so preoccupied with whether we _can_ that we don’t ask whether we _should_ .

In my experience this just doesn't happen.

> [@H2CO3](#):
>
> Instead, users should become experienced in using existing features, learn idioms, common patterns of refactoring (and antipatterns to be refactored), and adapt their own way of thinking to the language, if they want to deal in the language.

There's always a give and take. Expecting new users to just be completely assimilated is I think unrealistic. This certainly doesn't happen with natural languages and I don't see why formal ones would be much different.

I'd also note that this particular idiom comes from Haskell. Why is that relevant? Because Rust is similar to Haskell in many ways (at least when it doesn't pertain to strict/lazy...) and therefore it makes sense to ask whether something in Haskell would work well in Rust as well. That doesn't mean we automatically do it -- it needs to stand on its own merits, but Haskell is a good source of _inspiration_.

---

<div class="post-metadata">

**Author:** ![H2CO3](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/h2co3/32/2849_2.png) [@H2CO3](https://internals.rust-lang.org/u/H2CO3)\
**Post date:** [March 19, 2019, 12:06pm UTC](https://internals.rust-lang.org/t/pre-rfc-view-patterns/9628/11 "2019-03-19T12:06:06Z")

</div>

> [@Centril](#):
>
> Why do you assume their code is convoluted?

Because the example was. Testing for `is_some` and then unwrapping is strictly worse than pattern matching.

> [@Centril](#):
>
> Expecting new users to just be completely assimilated

It's not as drastic as that. I don't know what you are trying to say with "complete assimilation" (as if it were something aggressive or forced), but certainly learning how to program in an idiomatic manner in a certain language shouldn't be considered much of a hassle or an expectation too high, if someone wants to program in said language. Sure, people coming from different backgrounds will have different _styles,_ and it's fine; however, that in itself doesn't justify changing an important aspect of the language.

> [@Centril](#):
>
> I’d also note that this particular idiom comes from Haskell. […] That doesn’t mean we automatically do it – it needs to stand on its own merits, but Haskell is a good source of _inspiration_ .

I do realize that. I don't think I ever argued against this feature because it comes from Haskell, I didn't even mention it in fact. I argued against it because I think its benefits don't outweigh its drawbacks.

---

<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:** [March 19, 2019, 12:45pm UTC](https://internals.rust-lang.org/t/pre-rfc-view-patterns/9628/12 "2019-03-19T12:45:40Z")

</div>

> [@H2CO3](#):
>
> I argued against it because I think its benefits don’t outweigh its drawbacks.

That is all that is necessary, let's weigh the proposal on its merits... no need to add a "To be honest..." 😉All you need to say is "I don't think this contributes to readability/unsufficiently-common/covered-well-by-other-things..." and then we can have differences of opinion wrt. that.

---

<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:** [June 17, 2019, 12:45pm UTC](https://internals.rust-lang.org/t/pre-rfc-view-patterns/9628/13 "2019-06-17T12:45:40Z")

</div>

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