# Using \`if let\` to check for equality

**URL:** <https://internals.rust-lang.org/t/using-if-let-to-check-for-equality/11750>\
**Category:** Uncategorized\
**Created:** [February 4, 2020, 11:56pm UTC](https://internals.rust-lang.org/t/using-if-let-to-check-for-equality/11750 "2020-02-04T23:56:01Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![rsnakard-r7](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/rsnakard-r7/32/6863_2.png) [@rsnakard-r7](https://internals.rust-lang.org/u/rsnakard-r7)\
**Post date:** [February 4, 2020, 11:56pm UTC](https://internals.rust-lang.org/t/using-if-let-to-check-for-equality/11750/1 "2020-02-04T23:56:01Z")

</div>

Are you all aware you can use `if let` to check for equality? [https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=7e7a155eb38d168d78365ff4f11e0f1e](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=7e7a155eb38d168d78365ff4f11e0f1e)

Why does this work? Is it a quirk of pattern matching?

---

<div class="post-metadata">

**Author:** ![scottmcm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/scottmcm/32/2355_2.png) [@scottmcm](https://internals.rust-lang.org/u/scottmcm)\
**Post date:** [February 5, 2020, 12:34am UTC](https://internals.rust-lang.org/t/using-if-let-to-check-for-equality/11750/2 "2020-02-05T00:34:47Z")

</div>

Patterns are patterns. There's no need to prevent people from using simple ones.

That's the same feature as `match x { 1 => "one", _ => "something else" }`. And can be useful in slightly more complex cases, like `if let Ok(1) = my_result {` when you have a non-`PartialEq` error type and thus can't use `==`.

(Remember that `if let A = B { C } else { D }` is desugared into `match B { A => C, _ => D }`.)

---

<div class="post-metadata">

**Author:** ![rsnakard-r7](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/rsnakard-r7/32/6863_2.png) [@rsnakard-r7](https://internals.rust-lang.org/u/rsnakard-r7)\
**Post date:** [February 5, 2020, 12:40am UTC](https://internals.rust-lang.org/t/using-if-let-to-check-for-equality/11750/3 "2020-02-05T00:40:55Z")

</div>

Awesome, thanks a lot @scottmcm, I forgot about that desugaring.

---

<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:** [February 5, 2020, 12:45am UTC](https://internals.rust-lang.org/t/using-if-let-to-check-for-equality/11750/4 "2020-02-05T00:45:54Z")

</div>

(I think it's even preferable to use pattern matching above `==` when possible as it saves you from the bloat of `#[derive(PartialEq)]` and constructing the value to compare with `==` against, e.g. compare the MIRs in [https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=9dfbc17075a9595c94ab1506dd4772d4](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=9dfbc17075a9595c94ab1506dd4772d4)).

---

<div class="post-metadata">

**Author:** ![matklad](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/matklad/32/12266_2.png) [@matklad](https://internals.rust-lang.org/u/matklad)\
**Post date:** [February 5, 2020, 8:59am UTC](https://internals.rust-lang.org/t/using-if-let-to-check-for-equality/11750/5 "2020-02-05T08:59:36Z")

</div>

I wish we could just write `expr is pat` though 😃

---

<div class="post-metadata">

**Author:** ![djc](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/djc/32/1592_2.png) [@djc](https://internals.rust-lang.org/u/djc)\
**Post date:** [February 5, 2020, 9:16am UTC](https://internals.rust-lang.org/t/using-if-let-to-check-for-equality/11750/6 "2020-02-05T09:16:21Z")

</div>

Seconded. The inversion of the order in `if let <y> = <x>` does add some mental load for me.

---

<div class="post-metadata">

**Author:** ![kennytm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kennytm/32/161_2.png) [@kennytm](https://internals.rust-lang.org/u/kennytm)\
**Post date:** [February 5, 2020, 1:50pm UTC](https://internals.rust-lang.org/t/using-if-let-to-check-for-equality/11750/7 "2020-02-05T13:50:44Z")

</div>

> [@matklad](#):
>
> I wish we could just write `expr is pat` though 😃

[`matches!(expr, pat)`](https://doc.rust-lang.org/std/macro.matches.html) already exists (and will be stable starting from 1.42).

---

<div class="post-metadata">

**Author:** ![matklad](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/matklad/32/12266_2.png) [@matklad](https://internals.rust-lang.org/u/matklad)\
**Post date:** [February 5, 2020, 2:05pm UTC](https://internals.rust-lang.org/t/using-if-let-to-check-for-equality/11750/8 "2020-02-05T14:05:49Z")

</div>

I have mixed feelings about `matches!`.... Like, it certainly helps today, but I strongly feel that we need `is` which also introduces bindings into the part of the program dominated by the check (which at this point is such a boring idea that even [Java](https://openjdk.java.net/jeps/305) has it). With `is`, `matches!` is just a technical debt.

---

<div class="post-metadata">

**Author:** ![kennytm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kennytm/32/161_2.png) [@kennytm](https://internals.rust-lang.org/u/kennytm)\
**Post date:** [February 5, 2020, 5:02pm UTC](https://internals.rust-lang.org/t/using-if-let-to-check-for-equality/11750/9 "2020-02-05T17:02:14Z")

</div>

> [@matklad](#):
>
> I have mixed feelings about `matches!` .... Like, it certainly helps today, but I strongly feel that we need `is` which also introduces bindings into the part of the program dominated by the check (which at this point is such a boring idea that even [Java](https://openjdk.java.net/jeps/305) has it). With `is` , `matches!` is just a technical debt.

Problem of having `expr is Pat(binding)` is always about the scoping of those bindings (referring to [[Pre-Pre-RFC] Reviving Refutable Let [Runoff] - #9 by kennytm](https://internals.rust-lang.org/t/pre-pre-rfc-reviving-refutable-let-runoff/6672/9) again)

The JEP you mentioned is pretty short but skims lots of details, like how to determine the scope of `y` given `x instanceof Type y`. The [actual implementation](https://hg.openjdk.java.net/jdk/jdk/rev/7799a51dbe30) touches translation involving quite a number of constructs (`&&`, `||`, `!`, `?:`, `if`, `while`, `do while`, `for`), and contained some tests like these which I won't call them "boring" 🙃.

```java
// Test for (!e).T = e.F
if (!(!(o1 instanceof String s) || !(o3 instanceof Integer in))){
    s.length();
    i.intValue();
}

// Test for (e1 ? e2 : e3).F contains intersect(e1.T, e3.F)
if (o1 instanceof String s ? true : !(o1 instanceof String s)){
} else {
    s.length();
}

```

---

<div class="post-metadata">

**Author:** ![matklad](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/matklad/32/12266_2.png) [@matklad](https://internals.rust-lang.org/u/matklad)\
**Post date:** [February 5, 2020, 6:16pm UTC](https://internals.rust-lang.org/t/using-if-let-to-check-for-equality/11750/10 "2020-02-05T18:16:25Z")

</div>

There are two kinds of complexity here: cognitive complexity for the user, and implementation complexity for the implementor. My “boring” Java example refers strictly to the cognitive complexity. I agree that implementing this such that the user-visible behavior is obvious is non-trivial.

---

<div class="post-metadata">

**Author:** ![petrochenkov](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/petrochenkov/32/2784_2.png) [@petrochenkov](https://internals.rust-lang.org/u/petrochenkov)\
**Post date:** [February 5, 2020, 7:47pm UTC](https://internals.rust-lang.org/t/using-if-let-to-check-for-equality/11750/11 "2020-02-05T19:47:41Z")

</div>

@kennytm  
I have a [prototype implementation](https://github.com/rust-lang/rfcs/pull/2260#issuecomment-367158854) that defines some scoping rules (for names, as in name resolution) precisely in two variants - 1) until the end of the block (pretty weird) and 2) until the end of the statement (better).

The variable may be actually accessible in a smaller scope than it can be named in, due to move checker enforcing initialization, that "scope" may be non-continuous and is defined by NLL.

(The prototype doesn't support `||`.)

---

<div class="post-metadata">

**Author:** ![kennytm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kennytm/32/161_2.png) [@kennytm](https://internals.rust-lang.org/u/kennytm)\
**Post date:** [February 5, 2020, 7:58pm UTC](https://internals.rust-lang.org/t/using-if-let-to-check-for-equality/11750/12 "2020-02-05T19:58:42Z")

</div>

I think determining the scope of binding in Rust is similar to how uninitialized variables are tracked. The implementation complexity should already be a sunk cost (though explaining it in an RFC probably isn't 😉)

Though I do mean `expr is Pat(binding)` has cognitive complexity in determining the scope of the binding when negation is involved.

```rust
if !(expr is Some(x)) {
    // x cannot be used here...
    // but we are inside a block where the parent declared x?
    return;
}
...
dbg!(x); 
// x is usable here... 
// but i can't find where it is declared in the surrounding scope?

```

* * *

---

<div class="post-metadata">

**Author:** ![cuviper](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cuviper/32/1897_2.png) [@cuviper](https://internals.rust-lang.org/u/cuviper)\
**Post date:** [February 5, 2020, 8:49pm UTC](https://internals.rust-lang.org/t/using-if-let-to-check-for-equality/11750/13 "2020-02-05T20:49:24Z")

</div>

What if we said that `is` just doesn't support bindings -- go back to `if let` for that. But you could use `opt is Some(_)` just the same as `opt.is_some()`, without needing such dedicated methods for the task.

---

<div class="post-metadata">

**Author:** ![kennytm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kennytm/32/161_2.png) [@kennytm](https://internals.rust-lang.org/u/kennytm)\
**Post date:** [February 5, 2020, 8:52pm UTC](https://internals.rust-lang.org/t/using-if-let-to-check-for-equality/11750/14 "2020-02-05T20:52:13Z")

</div>

Then it is no different from `matches!(opt, Some(_))` (aside syntax)...

---

<div class="post-metadata">

**Author:** ![cuviper](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cuviper/32/1897_2.png) [@cuviper](https://internals.rust-lang.org/u/cuviper)\
**Post date:** [February 5, 2020, 8:53pm UTC](https://internals.rust-lang.org/t/using-if-let-to-check-for-equality/11750/15 "2020-02-05T20:53:46Z")

</div>

It wouldn't be the first time a language feature deprecated a library macro.

---

<div class="post-metadata">

**Author:** ![Ixrec](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ixrec/32/6754_2.png) [@Ixrec](https://internals.rust-lang.org/u/Ixrec)\
**Post date:** [February 5, 2020, 11:20pm UTC](https://internals.rust-lang.org/t/using-if-let-to-check-for-equality/11750/16 "2020-02-05T23:20:24Z")

</div>

IIRC the other times a language feature replaced a macro (`?` and `async/await` being the ones I'm familiar with), there were at least some corner cases that the language feature handled better than any macro ever could, especially at scale when several uses of the feature were nested across several crates.

But `is` and `matches!` would be exactly identical in behavior, if I understand the current proposals correctly, so adding an `is` keyword for this seems like a wash to me.

---

<div class="post-metadata">

**Author:** ![RalfJung](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ralfjung/32/2415_2.png) [@RalfJung](https://internals.rust-lang.org/u/RalfJung)\
**Post date:** [February 6, 2020, 2:33pm UTC](https://internals.rust-lang.org/t/using-if-let-to-check-for-equality/11750/17 "2020-02-06T14:33:57Z")

</div>

> [@kennytm](#):
>
> [`matches!(expr, pat)`](https://doc.rust-lang.org/std/macro.matches.html) already exists (and will be stable starting from 1.42).

Sounds like it'd be nice to have a clippy lint that proposes to use `matches!` instead of `if let <pat> = <expr>` if the pattern has no bindings... at least I feel like that will always strictly improve readability. `let` is very deeply connected with the idea of binding a name, at least in my brain. I always startle when I see things like `if let <const> = <expr>`.

---

<div class="post-metadata">

**Author:** ![cuviper](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cuviper/32/1897_2.png) [@cuviper](https://internals.rust-lang.org/u/cuviper)\
**Post date:** [February 7, 2020, 3:08pm UTC](https://internals.rust-lang.org/t/using-if-let-to-check-for-equality/11750/18 "2020-02-07T15:08:48Z")

</div>

> [@RalfJung](#):
>
> I always startle when I see things like `if let <const> = <expr>` .

It's a Rusty [Yoda condition](https://en.wikipedia.org/wiki/Yoda_conditions)! 🙂

---

<div class="post-metadata">

**Author:** ![tech6hutch](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/tech6hutch/32/6344_2.png) [@tech6hutch](https://internals.rust-lang.org/u/tech6hutch)\
**Post date:** [February 7, 2020, 9:36pm UTC](https://internals.rust-lang.org/t/using-if-let-to-check-for-equality/11750/19 "2020-02-07T21:36:24Z")

</div>

Yoda must have hit his head, though. `if let 42 = x {}` "if let forty-two equals x"

---

<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:** [February 8, 2020, 7:11pm UTC](https://internals.rust-lang.org/t/using-if-let-to-check-for-equality/11750/20 "2020-02-08T19:11:59Z")

</div>

Although it's kind of silly, you can even match on values in function arguments, if the values are [irrefutable](https://www.oreilly.com/library/view/rust-programming-by/9781788390637/e122b4d2-25d7-4cd6-8f43-2a2f519c1cda.xhtml):

```rust
enum Foo {
    TheOnlyVariant,
}

fn foo(Foo::TheOnlyVariant: Foo, (): ()) {}

```

[Next page](https://internals.rust-lang.org/t/using-if-let-to-check-for-equality/11750.md?page=2)
