JawguyChooser
JawguyChooser

Reputation: 1926

Why do I get the error "trait bounds were not satisfied" when using ReadBytesExt to read an integer from a slice of bytes?

The following Rust code doesn't compile.

extern create byteorder;
use byetorder::{LittleEndian, ReadBytesExt};

fn main() {                                                                          
    let buf: [u8; 12] = [                                                                     
        0x00, 0x42, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x30,               
    ];
    let id = &buf[0..1].read_u16::<LittleEndian>();              }

The message from the compiler:

error[E0599]: no method named `read_u16` found for type `[u8]` in the current scope           
  --> src/main.rs:18:25                                                                       
   |                                                                                          
18 |     let id = &buf[0..1].read_u16::<LittleEndian>();                                      
   |                         ^^^^^^^^                                                         
   |                                                                                          
   = note: the method `read_u16` exists but the following trait bounds were not satisfied:    
           `[u8] : byteorder::ReadBytesExt`

There are very similar questions on Stack Overflow, which I have reviewed, but mine is subtly different from those because I'm trying to read a u16 from a slice. In practice, I'm not sure why my example is substantively different, I'm sure I'm missing something obvious.

Specifically, it's not clear to me how what I've got is meaningfully different from what's in the accepted answer here:

How can I convert a buffer of a slice of bytes (&[u8]) to an integer?

Don't I also have &[u8] when I say &buf[0..1]?

Upvotes: 4

Views: 5362

Answers (2)

Sven Marnach
Sven Marnach

Reputation: 601421

Your code calls read_u16() on an array rather than a slice. You probably intend to call the method on &buf[0..1], but due to operator precedence you actually call it on buf[0..1]. This can be fixed by simply adding a pair of parentheses:

let id = (&buf[0..1]).read_u16::<LittleEndian>();

Your original code is interpreted as &(buf[0..1].read_u16::<LittleEndian>()), which is why the compiler complains that the ReadBytesExt trait is not implemented for [u8].

Upvotes: 7

Shepmaster
Shepmaster

Reputation: 430416

Because the trait bounds were not satisfied.

read_u16 needs the receiver to be a mutable reference:

pub trait ReadBytesExt: Read {
    fn read_u16<T: ByteOrder>(&mut self) -> Result<u16> { ... }
}

This is because it will call Read, which requires the same:

impl<'a> Read for &'a [u8] {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize>
}

Note that Read is implemented for &[u8], but takes &mut self. That means that self is a &mut &[u8], a mutable reference to a slice.


Introducing parenthesis, as suggested by Sven Marnach, creates a slice (&[u8]) as a temporary value and temporary values can be treated as implicitly mutable.

In the linked question, the starting variable is a slice that is mutable. Calling read_u16 can take a mutable reference to buf:

let mut buf: &[u8] = &[0x00, 0x42];

In your case, you have an array. While this dereferences to a slice, Rust's automatic (de)referencing rules will never add two levels of reference. Method lookup fails.

See also:

Upvotes: 0

Related Questions