2
#[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?

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
  • Does the hint `matches any value` under `empty` help? If not, it might be possible that there should be an issue to make it more prominent (now it is hidden between code lines a little, so might be simply unnoticed). – Cerberus Aug 03 '22 at 03:20
  • @Cerberus Fixed it ! (see answer below). Thx anyway – user13123535 Aug 03 '22 at 06:13
  • 1
    I mean, when the error message is unhelpful, Rust team might consider this a bug. – Cerberus Aug 03 '22 at 06:26

1 Answers1

2

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()
};
Silvio Mayolo
  • 62,821
  • 6
  • 74
  • 116
  • Thank you ! I wonder how I didn't notice this rule in the hours of coding in rust... – user13123535 Aug 03 '22 at 06:15
  • 1
    Easy mistake to make. Some languages (Elixir comes to mind) will let you "pin" a variable, so `xyz` is a pattern that binds a variable, but `^xyz` is a pattern that checks for equality. I'm not sure if this has been proposed in Rust in the past, but it would be nice to have. – Silvio Mayolo Aug 03 '22 at 15:34