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

3

12

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.

Forerun answered 23/7, 2019 at 19:45 Comment(1)
matches!(a, D::A(_))Holding
T
17

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:

Ticknor answered 23/7, 2019 at 19:51 Comment(1)
Man.. I still don't know why enums cannot be passed as parameters in Rust. It's really counter-intuitive.Cilka
T
4

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

Transferase answered 27/3, 2023 at 5:58 Comment(0)
R
1

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

Roadway answered 22/8 at 3:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.