1
votes

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?

2
I mean, it does give you a warning. Should unreachable code be an error?trent ᶠᵒʳᵐᵉʳˡʸ ᶜˡ
@trentcl I am not saying it should be an error necessarily but maybe I was expecting that Rust would never let a slip happen(Catches a lot issues at compile time) even if it was only a piece of dead code. But, yes the definition put up by Boiethios justifies it real crisp.Rajeev Ranjan

2 Answers

6
votes

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.

4
votes

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.