Unreachable pattern

Could somebody please explain to me, why DOG and BIRD are marked by Rust as unreachable patterns?

fn main() {
 let CAT:&str ="CAT";
 let DOG:&str ="DOG";
 let BIRD:&str ="BIRD";
    let value = "CAT";
    match value
        {
            CAT=>ANIMAL::CAT,
            DOG=>ANIMAL::DOG,
            BIRD=>ANIMAL::BIRD,
            _=>panic!("Not a recognized animal")
        };
}

enum ANIMAL
{
    CAT,
    DOG,
    BIRD,
}

A path pattern has to be an enum variant, const or associated const, or some type. Instead you've named some variables, which instead acts like an identifier. That is, you're unconditionally binding the value to new variables named CAT, DOG, and BIRD as if you did

let CAT = value;

(A match isn't just short for some runtime if-else like checks, which is why the compiler can check for exhaustiveness at compile time.)

Use consts for this purpose, not variables.

3 Likes

Hi and thank you for your reply. I of course don't argue, but try to actually understand what you've said.
This part:

This, I cannot wrap my head around it.

In that example variable CAT has a value, let's say "DOG". How is it that match won't match that value to a DOG?

Consider this example. When I pass in 123, it doesn't match the first three patterns. In the fourth pattern, the value is bound to a new variable x, which I then use in the print. That last pattern can't fail to match.

Your OP does something similar (the first pattern binds the value to a new variable CAT and can't fail to match -- which is why the other patterns are unreachable).

Because match doesn't compare variables, it destructures and compares values (literals or consts). The values in variables can vary at run time (i.e. aren't known at compile time), generally speaking; literals and consts cannot.

If you want to compare variables, use if-else.

if value == CAT {
    Animal::CAT
} else if value == DOG {
    Animal::DOG
} else if value == BIRD {
    Animal::BIRD
};

Or maybe

match value {
    _ if value == CAT => Animal::CAT,
    _ if value == DOG => Animal::DOG,
    _ if value == BIRD => Animal::BIRD,
    _ => panic!("..."),
}

But since the values you want to compare are all known at compile time, const (or literals) is the better option.

You can also read more about match in the book.

Exactly this^^^ which I am aware of.
So how is it that destructuring value:

let value = "DOG";
    match value

and comparing values literals, which here the literal is "DOG" I'm getting:

warning: unreachable pattern
 --> src/main.rs:9:13
  |
8 |             CAT=>ANIMAL::CAT,
  |             --- matches any value

This isn't something that is very intuitive in my opinion.

This stems from an ambiguous pattern syntax, which is an unfortunate gotcha.

Consider this code:

match 123 {
    a => { println!("a"); }
    b => { println!("b"); }
}

What does this mean?

Turns out it can mean two different things:

  1. Set the variable a to the value 123 and print "a". playground
  2. Compare the value 123 with the constant value a, and print "a" if it is equal, "b" if it is unequal. playground

The way the ambiguity is resolved is by whether a is a constant. If there is no such constant, interpretation 1 is used. If there is such a constant, interpretation 2 is used.

In your code, there is technically no constant CAT, even though you have an immutable variable CAT. The way it works is variables created by let don't count as compile-time constants. Therefore, interpretation 1 is used and match creates a new variable CAT that shadows the original variable CAT.

3 Likes

Thank you very much for your explanation. Now I understand. Tbh, I'm not sure why scenario 1 described by you was ever considered to make sense, but hey, I don't make the rules. I just live by them :wink:
Once again, thank you for your explanation, it really helped me to understand.

Well, you need some way to say that you want to create a new variable. For example:

match Some(123) {
    Some(x) => { println!("{x}"); }
    _ => {}
}

This allows you to extract the 123 into a new variable x (scenario 1). playground

But if x happens to be a constant, then it works differently (scenario 2): playground

It's unfortunate that the two meanings have the same exact syntax.

Hard to disagree :wink: