Comparison of [u8; n] and Option<&[u8]> using ==

I'm writing code that works with strings and want to compare [u8; n] and Option<&[u8]>. I wrote the code below, but I couldn't compile this.

let bytes = "text....".as_bytes();
let slice = bytes.get(1..3); // : Option<&[u8]>

if Some(b"0x") == slice {
//                ^^^^^ expected array `[u8; 2]`, found slice `[u8]`
//
//    note: expected enum `std::option::Option<&[u8; 2]>`
//             found enum `std::option::Option<&[u8]>
}

The two below could be compiled.

match expression

match slice {
    Some(b"0x") => {}
    _ => {}
}

Manually specify the type as &[u8] (instead of the inferred [u8; 2]).

let fixed_slice: &[u8] = b"0x";
if Some(fixed_slice) == slice {
    // compilable
}

Compared to the compilable ones, the first one also seems fine to me.
I wanted to write in the first way, but is it not compilable due to some restriction?

Unfortunately I don't think we can add PartialEq<Option<&[T]>> for Option<&[T; N]> (I think it would be a breaking change), but you could instead use if let, like so

if let Some(b"0x") = slice {
    
}

to do the same thing

1 Like

Or, to avoid the Yoda condition:

if matches!(slice, Some("b0x")) {

}
1 Like

Weirdly, simply switching the arguments around makes it compile:

// OK
if slice == Some(b"0x") { }

// error[E0308]: mismatched types
if Some(b"0x") == slice { }

playground

It seems that the &[u8; 2] is coerced into a &[u8] for only the former expression. My guess is that the compiler does not attempt any coercions for the receiver (left argument to == ), but can then coerce the right argument to have a matching type.

5 Likes

Side note, depending on what String-like things you need to do with bytes, you may find the bstr crate very useful.

1 Like

This is the simplest solution, but I hadn't tried it. I would use this. I didn't expect to be able to compile with this.
Thank you for answers. I now know that I can also write with if let or matches!.

There is also the unstable Option::contains:

if slice.contains(b"0x") {
    // ...
}
2 Likes

Alternatively you could use slice.starts_with() to check whether the slice has a certain prefix.

fn main() {
    let bytes = "text....".as_bytes();
    let slice = bytes.get(1..3).unwrap_or_default(); // None -> &[]
    
    if slice.starts_with(b"0x") {
        println!("Started with prefix");
    }
}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.