Why is this pattern unreachable?

Viewed 87
#[allow(dead_code)] 
fn print_grid(empty: (usize,usize), goal: (usize,usize), (w,h): (usize,usize)) {
    for y in 0..h {
        for x in 0..w {
            let s: String = match (x,y) {
                empty => "_".to_string(),
                goal => "G".to_string(),
                (0,0) => "*".to_string(),
                _ => ".".to_string()
            };
            print!("{s} ");
        }
        println!();
    }
}

This match statement generates an unreachable pattern warning:

warning: unreachable pattern
  --> src/lib.rs:75:17
   |
74 |                 empty => "_".to_string(),
   |                 ----- matches any value
75 |                 goal => "G".to_string(),
   |                 ^^^^ unreachable pattern
   |
   = note: `#[warn(unreachable_patterns)]` on by default

And indeed, when I use this function, any pattern will lead to "_" being printed.

It may be obvious but I can't figure it out. Is this an error from Rust (I doubt it)? Is there something important I'm missing?

1 Answers

Patterns in Rust can only compare against the shape of the data or literal values. So it's possible to pattern match against (for instance) the number 1 since that's a literal in the code, but not against a variable.

match (x,y) {
  empty => "_".to_string(),
  goal => "G".to_string(),
  (0,0) => "*".to_string(),
  _ => ".".to_string()
};

The names empty and goal here have no relation to the names empty and goal you already declared. They're new variables shadowing the old ones. So the first pattern will match anything and will assign it to a new variable called empty.

To compare against variables, use a guard clause.

match (x,y) {
  (0,0) => "*".to_string(),
  x if x == empty => "_".to_string(),
  x if x == goal => "G".to_string(),
  _ => ".".to_string()
};
Related