asosnovsky
asosnovsky

Reputation: 2235

How do I pass an enum variant to match on as a function parameter?

I would like to pass in the parameters what arm of the enum I need to match, something like this:

enum D {
    A(i64),
    B(u64),
    C(u64, u64),
}
let a = D.A(10);
println!(a.is_of(D.A)); // true
println!(a.is_of(D.B)); // false

I know I can use matching rules for this, but I'd like this is_of method to take as an input of the enum options for my purposes.

Upvotes: 12

Views: 15253

Answers (3)

June
June

Reputation: 11

It's possible to create an is_of function like you suggest, but with some caveats, and I don't recommend it.

In general, you can indeed pass enum variants as function parameters. While enum variants are not types, they are functions, and you can pass functions as parameters all you want. The problem is making an is_of function that can take any of your variants as an argument.

If all of your variants took the same type, you could do this:

#[derive(Clone, Copy, PartialEq)]
enum Foo {
    A(u64),
    B(u64),
    C(u64),
}
impl Foo {
    fn into_inner(self) -> u64 {
        match self {
            Foo::A(n) | Foo::B(n) | Foo::C(n) => n
        }
    }
    fn is_of(self, variant: fn(u64) -> Foo) -> bool {
        self == variant(self.into_inner())
    }
}


fn main() {
    assert!(Foo::A(10).is_of(Foo::A))
}

Importantly, is_of will take any function pointer that returns Foo, so you can pass in arbitrary functions with the correct signature in addition to the variants.

Alternatively, if all your variants are different types (as in your case), you can define a trait that will allow you to pass the different variants in.

enum Bar {
    A(u64),
    B(u32, i32),
}
impl Bar {
    fn is_of<V: Variant<Args>, Args>(self, variant: V) -> bool {
        V::check(self)
    }
}
trait Variant<Args> {
    fn check(this: Bar) -> bool;
}
impl<F: Fn(u64) -> Bar> Variant<(u64,)> for F {
    fn check(this: Bar) -> bool {
        matches!(this, Bar::A(_))
    }
}
impl<F: Fn(u32, i32) -> Bar> Variant<(u32, i32)> for F {
    fn check(this: Bar) -> bool {
        matches!(this, Bar::B(_, _))
    }
}

Obviously, this code is not really doing anything useful, and it opens you up to all sorts of bugs. If you were gonna do something like this, you would probably want to generate the trait definitions with a macro at least. And it has the same caveat as above that it'll accept any functions with matching signatures, not just the variants.

Playground link

Upvotes: 1

G&#252;nter Z&#246;chbauer
G&#252;nter Z&#246;chbauer

Reputation: 658057

The discriminant of an enum variant can be passed for compairson

use std::mem::{discriminant,Discriminant};

enum MyEnum {
  A,
  B,
}

fn is_enum_variant(value: &MyEnum, d: Discriminant<MyEnum>) -> bool {
  discriminant(value) == d
}

fn main() {
  println!("Is variant: {}", is_enum_variant(&MyEnum::A, discriminant(&MyEnum::A)));
  println!("Is variant: {}", is_enum_variant(&MyEnum::A, discriminant(&MyEnum::B)));
}

Rust Playground

Upvotes: 4

Shepmaster
Shepmaster

Reputation: 432009

You cannot.

  • It is not possible to pass types as function parameters.
  • Enum variants are not types to start with.

If you are OK using a macro instead of a function, see

See also:

Upvotes: 17

Related Questions