If-let but exclude the pattern

I wrote something like:

enum E {
    A,
    B,
    C,
}

fn foo(v: Option<E>) {
    if let Some(A) = v {} else {
        // stmts
    }
}

Maybe we can provide a syntax sugar like if let pat != v { ... }?

2 Likes

May not be sugary enough for you but you can write if !matches!(v, Some(A)) {…}.

9 Likes

Due to the slightly confusing appearance of the two exclamation marks around matches I might personally write this as

if !(matches!(v, Some(A))) {…}

with some extra parentheses.

9 Likes

There's always:

use std::ops::Not as _;

if matches!(v, Some(A)).not() {
    // ...
}
7 Likes

I mean why not just use let-else. It was introduced specifically for this purpose and stabilised in 1.65.0.

let Some(A) = v else {
// ... do smthing
}
println!("{A}");

let-else always needs to diverge in the else block. This might not be ergonomic in some cases.

8 Likes

This behavior could fully be resolved by feature which is called Not-pattern. It looks like that:

Pattern :     NotPattern | PatternNoTopAlt

NotPattern :  not PatternNoTopAlt

And examples:

if let not Some ( 5 ) = foo ()  {
    bar ()
}

let x = if let not 1 .. 8 = foo () { /*  */ };

It's far from ergonomic, but you can build this from let-else, e.g.

'label: {
    let Some(5) = foo() else {
        bar();
        break 'label;
    }
}

but truthfully I'd say if you don't want it to make any bindings (and thus require the else to diverge), just write it as

if matches!(foo(), Some(5)) == false {
    bar();
}

and be done with it. Clippy doesn't like it and wants !matches! instead, but clippy can be quieted, or you can import ops::Not and use .not() instead if you do this a lot.

This form is almost entirely useless, since value if-let must have an else, so it's just the difference

// between
let x = if let 1..8 = foo() { positive() } else { negative() };
// and
let x = if let not 1..8 = foo() { negative() } else { positive() };

and no more.

let-else was significant enough to be added because creating a refutable binding in the same scope is a reasonably common operation with a meaningful restriction to make it possible. let-else-match allowing you to recover the residual of the scrutinee might pass the significance bar. Not patterns almost certainly won't. People already aren't particularly fond of using [if-]let[-else] with bindless patterns, and a not pattern must be bindless by necessity.

On average, code doing this a lot is likely structured suboptimally, and could benefit from more specific types and/or parsing rather than validating.

Designing a language solution has never been the hard part here; the hard part is showing that the problem is significant enough to deserve a solution to be added to the language.

3 Likes

I would just write:

if !matches!(...)

For better or worse, Rust has decided to use ! for two different things: embrace it and enjoy the slightly funny notation.

5 Likes

This is neither serious, nor a technical contribution, nor helpful in any way... but... This prefix-negated matches in isolation reminds me of the "opening exclamation point" used in Spanish:

if ¡matches!(...)

:slight_smile:

9 Likes

And for those who really strongly care about not having to write !matches!(...), there is always

use std::ops::Not;

matches!(...).not()

Though for me personally, that's a bit much.

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