Как сравнить перечисление без соответствия шаблону

Я хочу применить filter на итераторе, и я придумал этот, и он работает, но он очень многословный:

.filter(|ref my_struct| match my_struct.my_enum { Unknown => false, _ => true })

Я бы скорее написал что-то вроде этого:

.filter(|ref my_struct| my_struct.my_enum != Unknown)

Это дает мне ошибку компиляции

binary operation `!=` cannot be applied to type `MyEnum`

Есть ли альтернатива сопоставлению подробных шаблонов? Я искал макрос, но не нашел подходящего.

Ответы

Ответ 1

Во-первых, вы можете использовать черту PartialEq, например, #[derive]:

#[derive(PartialEq)]
enum MyEnum { ... }

Тогда ваш "идеальный" вариант будет работать так, как есть. Однако для этого требуется, чтобы содержимое MyEnum также реализовало PartialEq, что не всегда возможно/требуется.

Во-вторых, вы можете реализовать макрос самостоятельно, что-то вроде этого (хотя этот макрос не поддерживает все виды шаблонов, например, альтернативы):

macro_rules! matches(
    ($e:expr, $p:pat) => (
        match $e {
            $p => true,
            _ => false
        }
    )
)

Затем вы будете использовать его следующим образом:

.filter(|ref my_struct| !matches!(my_struct.my_enum, Unknown))

Существует RFC, чтобы добавить такой макрос в стандартную библиотеку, но он все еще обсуждается.

Ответ 2

Я бы использовал сопоставление с образцом, но я бы переместил его к методу на перечислении, чтобы закрытие фильтра было более аккуратным:

#[derive(Debug)]
enum Thing {
    One(i32),
    Two(String),
    Unknown,
}

impl Thing {
    fn is_unknown(&self) -> bool {
        match *self {
            Thing::Unknown => true,
            _ => false,
        }
    }
}

fn main() {
    let things = vec![Thing::One(42), Thing::Two("hello".into()), Thing::Unknown];
    for t in things.iter().filter(|s| !s.is_unknown()) {
        println!("{:?}", t);
    }
}

См. также: