Skip to content

Macroless mapping of patterns -> bool #2153

Description

@alexheretic

The current if let $pattern = $expr {} functionality is great when you're de-structuring parts of the pattern for use in the if-scope. However, when matching without that need it can feel quite limited. I would suggest allowing macroless evaluation of certain pattern matches -> bool.

Current State

Example compile error:

enum Example {
    A(i32, &'static str),
    B(i32, bool),
}

let example = Example::A(123, "alex");
let always_do_it = true;

if let Example::A(.., "alex") = example || always_do_it {
    // compile error!
}

This makes sense when you can reference part of the pattern within the if-scope, but less so when it's a straight forward match.

The macroless way of mapping a pattern match to a boolean can currently be a little ugly.

if if let Example::A(.., "alex") = example { true } else { false} || always_do_it {
    // works, but ugly enough to have to split out
}

let examples: IntoIter<Example> = ...

// filter away the A-123 cases, difficult to read
examples.filter(|t| if let Example::A(123, ..) = *t { false } else { true });

New functionality

How about we allow pattern matches without a variable binding to be evaluated as bools.

let is_a_123: bool = let Example::A(123, ..) = example;

This would mean the above examples would start working / could be more readable.

if let Example::A(.., "alex") = example || always_do_it {
    // works now
}

examples.filter(|t| !let Example::A(123, ..) = *t) // nicer

Its worth noting explicitly that I would expect patterns with variable bindings to not be allowed

// compile error! variable bindings are not allowed in pattern -> bool evaluations
let is_a_123: bool = let Example::A(123, some_str) = example;

This means this functionality would have a clear distinction from the current if let $pattern = $expr {} usage.

Working example

I'd like this as macroless functionality built into the language. However, I can provide a simple macro for a working example.

macro_rules! iff { 
    (let $p:pat = $e:expr) => {{
        if let $p = $e { true } else { false }
    }};
}

let is_a_123: bool = iff!(let Example::A(123, ..) = example);

if iff!(let Example::A(.., "alex") = example) || always_do_it {}

examples.filter(|t| !iff!(let Example::A(123, ..) = *t));

These are not nice, but show the functionality working as close as we can currently get.

Comments

I think this would make a good addition to the current if let behaviour. So please fill me in on all the obvious stuff I've missed!

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    T-langRelevant to the language team, which will review and decide on the RFC.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions