28

The following code (playground)

let max_column = 7;
edge = match current_column {
    0 => Edge::Left,
    max_column => Edge::Right,
    _ => Edge::NotAnEdge
};

results in the following warning:

warning: unreachable pattern
  --> src/main.rs:10:9
   |
9  |         max_column => Edge::Right,
   |         ---------- matches any value
10 |         _ => Edge::NotAnEdge
   |         ^ unreachable pattern
   |
   = note: #[warn(unreachable_patterns)] on by default

Replacing the variable max_column with the literal works fine:

let max_column = 7;
edge = match current_column {
    0 => Edge::Left,
    7 => Edge::Right,
    _ => Edge::NotAnEdge
};

Why is _ unreachable in the first example when it can be reached for any values where current_column != max_column?

trent
  • 25,033
  • 7
  • 51
  • 90
Mike Vella
  • 10,187
  • 14
  • 59
  • 86

1 Answers1

26

The Rust Programming Language explains how a match expression is processed, emphasis mine:

When the match expression executes, it compares the resulting value against the pattern of each arm, in order.

In your example, max_column is the name of the variable to be bound to, not a constant or an outside variable. When the compiler reaches max_column, any remaining values will be assigned to that match arm, making subsequent arms unreachable.

In your case, you want to make max_column a real constant:

let current_column = 1;
const MAX_COLUMN: i32 = 7;
edge = match current_column {
    0          => Edge::Left,
    MAX_COLUMN => Edge::Right,
    _          => Edge::NotAnEdge
};

Or if that's not possible, you want a match guard:

let current_column = 1;
let max_column = 7;
edge = match current_column {
    0                    => Edge::Left,
    a if a == max_column => Edge::Right,
    _                    => Edge::NotAnEdge
};

Note that, as a first approximation, a and _ are the same thing in this case! In both cases, the matched variable will be bound to a name (a or _ respectively), but any identifier prefixed with _ is special-cased to be used as an unused variable placeholder.

bluss clarifies and corrects this approximation:

_ is a separate special case, it's not a variable binding at all, but it is the absence of one! Matching against _x moves the value into _x, _ does no such thing. (The difference is observable.)

mcarton
  • 27,633
  • 5
  • 85
  • 95
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 7
    @MikeVella Well, the alternative is that a `x => /* the default case, using x */` match arm stops becoming a default case when a name `x` is introduced in any visible scope. That's inconsistent and, to me, counter-intuitive as well. –  Jan 29 '15 at 23:42
  • 4
    `_` is a separate special case, it's not a variable binding at all, but it is the absence of one! Matching against `_x` moves the value into `_x`, `_` does no such thing. (The difference is observable.) – bluss Jan 30 '15 at 00:39
  • 2
    Would be nice if there was a way to "pin" a variable [as in Elixir](https://elixir-lang.org/getting-started/pattern-matching.html#the-pin-operator). In this case, `^ MAX_COLUMN =>` would mean, "match against this variable" instead of "assign value to this variable name". – richardpringle Apr 27 '20 at 13:58
  • @richardpringle that doesn't make sense with the semantics of `match`. Match patterns must be verified at **compile time**, so having a variable cannot work as it would need to be checked at runtime. That's why a `const` works — it's known at compile time. `static` and `let` variables can change during program execution. The match guard syntax calls out that the check is moving from compile time to runtime. – Shepmaster Apr 27 '20 at 14:59
  • 2
    I'm saying that a little syntactic sugar would be nice. `^x => {}` would desugar to `y if y == x => {}`. This new syntax would also call out that the check is moving from compile-time to runtime. It would cut down on boilerplate at the cost of adding another operator. Not sure if you could accomplish the same thing with a macro... – richardpringle Apr 28 '20 at 16:55