Non-exhaustive matching on tuples of different enum types

Hi everyone,
I have an application in which I would like to take pairs of different enum types as inputs and map different pairs of their variants onto certain actions. For example, consider the following code (playground link):

#[allow(dead_code)]
enum Enum1 {
    Var1,
    Var2,
    Var3,
}

#[allow(dead_code)]
enum Enum2 {
    Var1,
    Var2,
}

fn main() {
    let e1 = Enum1::Var1;
    let e2 = Enum2::Var2;

    match (e1, e2) {
        (Enum1::Var1, Enum2::Var1) => println!("1, 1"),
        (Enum1::Var2, Enum2::Var2) => println!("2, 2"),
        (Enum1::Var3, Enum2::Var1) => println!("3, 1"),
        _ => panic!("Invalid pair of variants")
    }
}

In main, the match statement is effectively creating a function that maps a tuple of a variant of Enum1 and a variant of Enum2 onto a specific side effect. Importantly, note that this function is not exhaustive over all pairs of variants (at least, it is not semantically valid). For example, the code above will panic because the tuple (Enum1::Var1, Enum2::Var2) is matched by the _ pattern.

The problem with this construct is that it can be very easy to forget to add additional patterns to the match statement as new variants are added to either Enum because the compiler still sees an exhaustive match due to the _ pattern; one must always remember to update the match statement everywhere this construct is used to accommodate the new variants.

Is there a way to solve this problem using Enums? Or would it be better to take a different approach?

To ultimately solve my problem, I will need

  • to compare the values of two different types and decide what to do on a subset of the range of all possible input value pairs
  • fail to compile if I have not somehow accounted for a newly-introduced value

Thanks for the help everyone!

If you explicitly list every case, it will error when you add new variants to the enums.

1 Like
#[allow(dead_code)]
enum Enum1 {
    Var1,
    Var2,
    Var3,
}

#[allow(dead_code)]
enum Enum2 {
    Var1,
    Var2,
}

fn main() {
    let e1 = Enum1::Var1;
    let e2 = Enum2::Var2;

    match (e1, e2) {
        (Enum1::Var1, Enum2::Var1) => println!("1, 1"),
        (Enum1::Var2, Enum2::Var2) => println!("2, 2"),
        (Enum1::Var3, Enum2::Var1) => println!("3, 1"),
        (Enum1::Var1, Enum2::Var2) |
        (Enum1::Var2, Enum2::Var1) |
        (Enum1::Var3, Enum2::Var2) |
        => panic!("Invalid pair of variants")
    }
}

Thank you @alice and @Hyeonu. So if I understand correctly the only alternative in this case is to explicitly list all the cases instead of matching on the _ pattern? I suppose that will have to work then for now.

Thanks again.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.