Help with matching slices against byte array constants

Hello!
I am writing a very simple message dispatch function, which I've mocked up in playground below. I am referencing message names imported from bindgen, which come as references to const arrays. I'd like to use these constants in a match in order to branch to the right handler. However, the comparisons aren't satisfying the compiler.

Is there a pretty way to make this work? I know I can do bar_name if bar_name == BAR_NAME => inside the match in order to get what I want, but it's a bit wordy, especially compared to the brevity of FOO_NAME below.

Of course if I have to spell it out for the compiler, I will do it that way. But a pretty way forward would be much appreciated!

const FOO_NAME: &[u8] = b"foo";
const BAR_NAME: &[u8; 3] =  b"bar";  // this is the form in which I have the constants

fn handle_msg(name: &[u8]) {
    match name {
        FOO_NAME => println!("got foo"), // this works
        BAR_NAME => println!("got bar"),  // this fails to compile
        _ => println!("got unknown msg"),
    }
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
 --> src/lib.rs:7:9
  |
2 | const BAR_NAME: &[u8; 3] =  b"bar";
  | ------------------------ constant defined here
...
5 |     match name {
  |           ---- this expression has type `&[u8]`
6 |         FOO_NAME => println!("got foo"),
7 |         BAR_NAME => println!("got bar"),
  |         ^^^^^^^^
  |         |
  |         expected `&[u8]`, found `&[u8; 3]`
  |         `BAR_NAME` is interpreted as a constant, not a new binding
  |         help: introduce a new binding instead: `other_bar_name`
  |
  = note: expected reference `&[u8]`
             found reference `&'static [u8; 3]`

For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` due to previous error

I'm sure someone else will be along soon with a better solution, but Rust Playground works for me, by converting BAR_NAME into a slice inside the function. The relevant trick is:

const FOO_NAME: &[u8] = b"foo";
const BAR_NAME: &[u8; 3] =  b"bar";  // this is the form in which I have the constants

fn handle_msg(name: &[u8]) {
    const INNER_BAR_NAME: &[u8] = BAR_NAME.as_slice();
    match name {
        FOO_NAME => println!("got foo"), // this works
        INNER_BAR_NAME => println!("got bar"),  // this fails to compile
        _ => println!("got unknown msg"),
    }
}

This works because INNER_BAR_NAME is of type &[u8], and Rust will let you match an &[u8] against another &[u8], but won't automatically convert &[u8; 3] into &[u8] for the match to use (for reasons I'm not clear on).

1 Like

Thanks for the suggestion! I think this is a bit too wordy for me - imagine a dispatch function with 25 or 50 such names, so this ends up being a bit much.
I too am curious why the slice type is OK for a pattern match and why a ref to array isn't!

You can get a bit of a handle on what's going on by asking Rust how big the two different types are:

dbg!(std::mem::size_of::<&[u8]>());
dbg!(std::mem::size_of::<&[u8; 3]>());

The second type is smaller than the first because it doesn't contain a length usize internally, since the compiler knows that automatically. But Rust's pattern matching won't automatically turn arrays into slices, so you'll need to find some other way to match them in a single pattern.

1 Like

Patterns aren't expressions, basically, and const support is a special thing. (And the compiler doesn't know if you made a mistake or not; a match on &[u8; 3] can be exhaustive without a wildcard match for example.)

This is a use case for inline const.

    match name {
        FOO_NAME => println!("got foo"),
        const { BAR_NAME.as_slice() } => println!("got bar"),
        _ => println!("got unknown msg"),
    }
1 Like