10

I have an issue with the following Rust code:

pub fn median(v: &Vec<i32>) -> f32 {
    let len = v.len();
    match len % 2 {
        0 => (v[len / 2 - 1] + v[len / 2]) as f32 / 2 as f32,
        1 => v[(len - 1) / 2] as f32,
    }
}

This code doesn't compile due to a 'Non exhaustive patterns' error. Why is that? What does the % operator return?

Peter Hall
  • 53,120
  • 14
  • 139
  • 204
fresh
  • 381
  • 2
  • 11

2 Answers2

11

The compiler is not smart enough to figure out that the result of len % 2 can only ever be 0 or 1. It demands a match arm for cases where the result is some other value. You can solve this by explicitly saying that those cases are impossible:

match len % 2 {
    0 => (v[len / 2 - 1] + v[len / 2]) as f32 / 2 as f32,
    1 => v[(len - 1) / 2] as f32,
    _ => unreachable!()
}

The _ will match any other value not previously mentioned. The unreachable!() tells the compiler "this code will never execute", but cause a panic!() just in case it does in fact execute. That way, the program is correct all the time at practically no cost.

Future versions of the compiler might figure out that the values 2.. or not possible.

The % is the remainder operator (not to be cofused with the mod-operator).

user2722968
  • 13,636
  • 2
  • 46
  • 67
  • 1
    > The compiler is not smart enough to figure out that the result of len % 2 can only ever be 0 or 1. Yes, it is. But the type system requires that match arms be exhaustive over the whole type, even when the compiler can figure out that it's not necessary. – David A Sep 17 '19 at 02:05
  • 3
    As a quick sidenote: In release mode, the compiler can detect, that only `0` and `1` is possible and therefore eleminate the `_` branch completly. https://rust.godbolt.org/z/PVgltd – hellow Sep 17 '19 at 07:45
7

The simplest fix is to use _ instead of 1:

match len % 2 {
    0 => (v[len / 2 - 1] + v[len / 2]) as f32 / 2 as f32,
    _ => v[(len - 1) / 2] as f32,
}

Because len is odd and non-negative in the second branch, (len - 1) / 2 is the same as len / 2. I would simplify it like this:

let mid = v.len() / 2;
match v.len() % 2 {
    0 => (v[mid - 1] + v[mid]) as f32 / 2.0,
    _ => v[mid] as f32,
}
Aloso
  • 5,123
  • 4
  • 24
  • 41