1

In Rust I have an enum MatchType

enum MatchType {
    Exact,
    Lower,
    Greater,
    Other,
}

I use it for my compare function to compare two String. If MatchType is Exact/Lower/Greater I want to convert both String to f64 and compare their values (In case parse failed I consider comparison result to be false).

I can easily achieve it with some boilerplate code like so:

fn compare(str_one: String, str_two: String, match_type: MatchType) -> bool {
    match match_type {
        MatchType::Exact => {
            let f_one = str_one.parse::<f64>();
            let f_two = str_two.parse::<f64>();

            if let (Ok(f_one), Ok(f_two)) = (f_one, f_two) {
                f_one == f_two
            } else {
                false
            }
        },
        MatchType::Lower => {
            let f_one = str_one.parse::<f64>();
            let f_two = str_two.parse::<f64>();

            if let (Ok(f_one), Ok(f_two)) = (f_one, f_two) {
                f_one < f_two
            } else {
                false
            }
        },
        MatchType::Greater => {
            let f_one = str_one.parse::<f64>();
            let f_two = str_two.parse::<f64>();

            if let (Ok(f_one), Ok(f_two)) = (f_one, f_two) {
                f_one > f_two
            } else {
                false
            }
        },
        MatchType::Other => false,
    }
}

But I wanted to try something more concise: First check if the match is one of those three Exact/Lower/Greater then parse strings and then perform the actual comparison like so:

fn compare(str_one: String, str_two: String, match_type: MatchType) -> bool {
    match match_type {
        MatchType::Exact | MatchType::Lower | MatchType::Greater => {
            let f_one = str_one.parse::<f64>();
            let f_two = str_two.parse::<f64>();

            if let (Ok(f_one), Ok(f_two)) = (f_one, f_two) {
                match match_type {
                    MatchType::Exact => f_one == f_two,
                    MatchType::Lower => f_one < f_two,
                    MatchType::Greater => f_one > f_two,
                    // --- Non-exhaustive --- 
                    _ => false,
                }
            } else {
                false
            }
        },
        MatchType::Other => false,
    }
}

It works, but the inner match is non-exhaustive, of course simple _ => false fixes it, but I was wondering if I could somehow tell Rust compiler that the inner pattern is only those 3 enum variants Lower/Greater/Exact.

I tried to use pattern binding like so:

fn compare(str_one: String, str_two: String, match_type: MatchType) -> bool {
    match match_type {
        float_match @ (MatchType::Exact | MatchType::Lower | MatchType::Greater) => {
            let f_one = str_one.parse::<f64>();
            let f_two = str_two.parse::<f64>();

            if let (Ok(f_one), Ok(f_two)) = (f_one, f_two) {
                match float_match {
                    MatchType::Exact => f_one == f_two,
                    MatchType::Lower => f_one < f_two,
                    MatchType::Greater => f_one > f_two,
                    // --- Non-exhaustive error here ---
                }
            } else {
                false
            }
        },
        MatchType::Other => false,
    }
}

But I still get non-exhaustive error. Any way to deal with it?

Slava.In
  • 597
  • 1
  • 9
  • 22

2 Answers2

1

Maybe you could leave the boilerplate _ => false as is, but then it would allow to shortcut the if statement

fn compare(str_one: String, str_two: String, match_type: MatchType) -> bool {
    match match_type {
        MatchType::Exact | MatchType::Lower | MatchType::Greater => {
            let f_one = str_one.parse::<f64>();
            let f_two = str_two.parse::<f64>();

            match (f_one, f_two, match_type) {
                (Ok(f_one), Ok(f_two), MatchType::Exact)   => f_one == f_two,
                (Ok(f_one), Ok(f_two), MatchType::Greater) => f_one > f_two,
                (Ok(f_one), Ok(f_two), MatchType::Lower)   => f_one < f_two,
                _ => false
            }
        },
        MatchType::Other => false,
    }
}
Alexey S. Larionov
  • 6,555
  • 1
  • 18
  • 37
1

This is not possible. However, this pattern (matching against part of the values inside another match) is fairly common. Usually I see it with a panic (unreachable!()) on impossible variants, but returning a default value is also fine.

There are pattern types as an experimental work in this area that might produce something somewhere in the future, but it's still in the planning stage.

Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77