Overly limited `match ..`

fn cmp<T: PartialOrd, R>(x: T, y: T, smaller: R, equal: R, greater: R) -> R {
    if x < y { smaller }
    else if x == y { equal }
    else { greater }
}

if rewritten as an easier to read, semantically identical

fn cmp<T: PartialOrd, R>(x: T, y: T, smaller: R, equal: R, greater: R) -> R {
    match x {
        ..y => smaller,
        y   => equal,
        _   => greater
    }
}

suddenly PartialOrd is not good enough. It wants to accept only char and numeric types for y.

You could use partial_cmp directly and inspect the ordering:

fn cmp<T: PartialOrd, R>(x: T, y: T, smaller: R, equal: R, greater: R) -> R {
    use std::cmp::Ordering::{Less, Equal};
    match x.partial_cmp(&y) {
        Some(Less)  => smaller,
        Some(Equal) => equal,
        _           => greater
    }
}
3 Likes

Ok. At the same time this work-around is a lot more cruft than the if-cascade I wanted to avoid.

The question is more: why are equivalent structures not interchangeable? To me that seems like a lack of consistency in the language design.

Your example also doesn't work for char:

fn cmp<R>(x: char, y: char, smaller: R, equal: R, greater: R) -> R {
    match x {
        ..y => smaller,
        y   => equal,
        _   => greater
    }
}
error[E0080]: runtime values cannot be referenced in patterns
 --> src/lib.rs:3:11
  |
3 |         ..y => smaller,
  |           ^

I agree it would be nice if it also worked for constants of user-defined types.

Patterns cannot refer to runtime values. That's a consistent restriction. Guard clauses are for that. Something like x => foo(x) is always a catch-all pattern that introduces a new binding x rather than referring to some existing x. Except when x is an in-scope const or an enum variant. A range pattern like ..x is a sort of special case because it does not introduce a new binding and always expects x to be a constant.

8 Likes

Not sure what about it is "cruft"? I just took your match function and made the comparison explicit. It did go up from 5 lines to 6 if that is what you mean.

Wow! So even the error I got was misleading:

error[E0029]: only `char` and numeric types are allowed in range patterns
 --> src/main.rs:3:11
  |
3 |         ..y => smaller,
  |           ^ this is of type `T` but it should be `char` or numeric
1 Like

That looks like a bug. You should report that as an issue on rust-lang/rust, labeled as a diagnostic issue, and mention that the error was misleading and you'd expect to instead get something telling you that you can't use a runtime value in a pattern match.

9 Likes

bug report and propose fix for this beginner’s trap

1 Like