Rajeev Ranjan
Rajeev Ranjan

Reputation: 4096

Why does a match expression not report an error for a catch-all arm (_) prior to other arms?

Rust has a construct called match which looks very similar to switch in other languages. However I observed a very peculiar behavior of match.

let some_u32_value = 3;
match some_u32_value {
    _ => (),
    3 => println!("three"),
}

match respects the order in which the cases/patterns are mentioned. Why does it not report an error if the default (_) case is at the top? It does give a warning though:

warning: unreachable pattern
 --> src/main.rs:5:9
  |
5 |         3 => println!("three"),
  |         ^
  |
  = note: #[warn(unreachable_patterns)] on by default

A similar construct in Java, the switch, does not preserve any order, so having a default prior to other cases is not an error (ignoring the fall through behavior).

int x = 0;

switch (x) {
  default:
    System.out.println("default");
    break;
  case 0:
      System.out.println("Zero");
} 

Is there some purpose for doing this explicitly?

Upvotes: 1

Views: 1609

Answers (2)

Boiethios
Boiethios

Reputation: 42829

An unreachable pattern is not strictly an error, I mean: it does not prevent the compiler from "understanding" the code nor does it make the code unsafe.

Similarly, in C, for example, you can return a reference to a local variable without triggering an error (at least with gcc):

#include <stdio.h>

int* foo() {
    int x = 0;

    return &x;
}

int main() {
    printf("%d", *foo());

    return 0;
}

Generally, you should not consider a warning as "oh, that's only a warning, I don't care". A warning is an actual useful advice/information given by the compiler.

I like the definition given in :

A warning is often issued on recognizing a potential high-risk situation, a probable misunderstanding, degraded service or imminent failure.

because it helps to understand the difference between an error and a warning:

  • An error is an error,
  • A warning is a potential/probable error or a problematic thing.

In this situation, the last arm of the match is some dead code, so the compiler reports it accordingly.

Upvotes: 6

Peter Hall
Peter Hall

Reputation: 58805

Rust's match expressions are a lot more powerful than Java switch statements, and you can do a lot more than just matching numbers.

In particular, it supports pattern matching which lets you match pieces of your data, based on either its structure or the values it contains. When you have more complex patterns to match, it's important to be able to specify the order because the patterns may overlap. For example:

let value = Some((Some(3), "hello"));

let s = match value {
    None => "Nothing there!".to_owned(),
    Some((Some(3), _)) => "The number is exactly 3!".to_owned(),
    Some((Some(n), _)) if n > 3 => format!("Got a number bigger than 3: {}", n),
    Some((None, msg)) => format!("Message with no number: {}", msg),
    Some((_, msg)) => format!("Got the message, ignore the rest: {}", msg),
    _ => "Anything else?".to_owned()
};

println!("result = {}", s);

The last case here is actually impossible because the other branches cover everything. The compiler will give a warning, in case that isn't what you intended.

Upvotes: 4

Related Questions