Skip to content

Macroless mapping of patterns -> bool #2153

@alexheretic

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!

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