More elegant way to combine pattern matching and boolean condition

I just wrote this method on a struct:

    pub fn tag(&self, key: &str) -> Option<&Attribute> {
        self.tags().find(|attr| {
            if let Attribute::Unknown { name, .. } = attr
                && name == key
            {
                true
            } else {
                false
            }
        })
    }

I immensely dislike the if condition then true else false antipattern.
However,

    pub fn tag(&self, key: &str) -> Option<&Attribute> {
        self.tags().find(|attr| {
            let Attribute::Unknown { name, .. } = attr && name == key
        })
    }

is syntactically invalid.
And

    pub fn tag(&self, key: &str) -> Option<&Attribute> {
        self.tags().find(|attr| {
            if let Attribute::Unknown { name, .. } = attr {
                name == key
            } else {
                false
            }
        })
    }

still gives me antipattern vibes.
Is there a better way to write the above filter?

Early return maybe? I do this quite frequently in boolean returning functions and closures:

pub fn tag(&self, key: &str) -> Option<&Attribute> {
    self.tags().find(|attr| {
        let Attribute::Unknown { name, .. } = attr else { return false; }
        name == key
    })
}
2 Likes

A normal match might be the best solution here. Or some variant of the following, although I don't find it very readable due to the asymmetry.

let Attribute::Unknown { name, .. } = attr else { return false }
name == key

There's also the enum_extract macro that's like matches!() but allows destructuring, letting you write something like

// the macro evaluates to  `Some(name)` if the pattern matches
extract!(attr, Attribute::Unknown { name, .. } if name = key).is_some()
1 Like

The builtin way to compactly write a short match where branches map directly to true/false is to use the matches! macro mentioned by @jdahlstrom in passing above.

self.tags().find(
    |attr| matches!(attr, Attribute::Unknown { name, .. } if name == key)
)
11 Likes

Huh, good catch! Somehow I totally overlooked that you don’t actually need the name outside the pattern and that matches supports guards.

Even better. I don't know why I forgot about matches!. :smiley:

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.