Search code examples
rustcompiler-errorstrait-bounds

E0277: Is there a way to resolve trait bound ambiguity in use of AsRef with fixed-size array type? (e.g. `impl AsRef<[u8; 3]>`)


I'm trying to make the impl AsRef<[u8; 3]> to work in function parameters.

Playgrounds repro: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=e99007b0571ed2f088b3e38a6692ccdf

Here is the MPE:

fn print_bytes_3(bytes: impl AsRef<[u8; 3]>)
{
    println!("{:?}", bytes.as_ref());
}

pub fn main() {
    let a: [u8; 3] = [1, 2, 3];
    print_bytes_3(a);
}

Above code fails to compile with this error message:

Compiling playground v0.0.1 (/playground)
error[E0277]: the trait bound `[u8; 3]: AsRef<[u8; 3]>` is not satisfied
 --> src/main.rs:8:19
  |
8 |     print_bytes_3(a);
  |     ------------- ^ the trait `AsRef<[u8; 3]>` is not implemented for `[u8; 3]`
  |     |
  |     required by a bound introduced by this call
  |
  = help: the following other types implement trait `AsRef<T>`:
            [T; N]
            [T]
note: required by a bound in `print_bytes_3`
 --> src/main.rs:1:30
  |
1 | fn print_bytes_3(bytes: impl AsRef<[u8; 3]>)
  |                              ^^^^^^^^^^^^^^ required by this bound in `print_bytes_3`

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

AsRef documentation writes that the trait implements for a generic fixed-size array:

impl<T, const N: usize> AsRef<[T]> for [T; N]


If I'm understanding it correctly, it seems the compiler fails to determine which implementation of AsRef to use.

Following the error message, it's conflicting with other trait implementation, which is impl<T> AsRef<[T]> for [T]:

  = help: the following other types implement trait `AsRef<T>`:
            [T; N]
            [T]

After some tweaks, this code works:

fn print_bytes_3(bytes: &[u8; 3])
{
    println!("{:?}", bytes);
}

pub fn main() {
    let a: [u8; 3] = [1, 2, 3];
    print_bytes_3(&a);
}

However, I still want to take advantage of what AsRef gives: impl AsRef<[u8; 3]> in my function parameter as it can accept both owned and borrowed type without specifying &.

Is there a way to resolve this ambiguity that the compiler cannot determine? Or let me know if I'm doing something wrong (I'm new to Rust).


Solution

  • The problem is that you want a &[u8;3] but the implementation AsRef<[u8;3]> does not exist for arrays, you're pointing to the AsRef<[u8]> definition instead. Your code runs if you just remove the ;3 from your function declaration:

    fn print_bytes_3(bytes: impl AsRef<[u8]>) {
        println!("{:?}", bytes.as_ref());
    }
    

    If you want to take only a fixed size you should just take an array, if their elements implement Copy so does the array:

    fn print_bytes_3(bytes: [u8; 3]) {
        println!("{:?}", bytes);
    }