3

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]?

JawguyChooser
  • 1,816
  • 1
  • 18
  • 32
  • The Rust code you have provided isn't syntactically correct. – Shepmaster Jan 09 '19 at 21:43
  • Is there some syntax error which the compiler isn't telling me about? When I add the parans to get operator precedence correct (as in the accepted answer), the code compiles and runs as expected now. I'm missing your point, I guess. – JawguyChooser Jan 09 '19 at 22:45
  • Those are two separate comments. One is that your MCVE is incorrect and invalid. The other is pointing out how you can answer the question. You need to figure out for what types the trait is implemented and then the answer falls out. – Shepmaster Jan 09 '19 at 23:44

2 Answers2

6

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].

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
  • In many places in Rust, arrays decay gracefully into slices. Why does that not help the OP here? – Shepmaster Jan 09 '19 at 21:49
  • 1
    @Shepmaster The `read_u16()` method accepts its receiver by mutable reference, and method resolution only searches for `&T` and `&mut T`, but not for `&mut &T`. But you surely know this? – Sven Marnach Jan 09 '19 at 21:53
  • @Shepmaster I thought about this for a bit, but I couldn't think of _any_ situaion where the type `[T]` decays to `&[T]`. Could you give an example? The only vaguely related case I can think of is calling a method on a slice that accepts the receiver by value, which would indeed work for `[T]`. – Sven Marnach Jan 09 '19 at 22:09
  • 1
    there's a great many things I'm pretty sure I know, sure, but double checking never hurts. It was intended more as a "this information would be useful in the answer", but I've now included it in mine so you can keep your answer succinct. – Shepmaster Jan 09 '19 at 22:10
  • pedantically, I meant `[T; N]` (an array, not a slice not behind a pointer), but I think you meant the same as me. I was mostly handwaving at towards methods on slice which are implemented with `&self` and are accessible via `Deref` but also via coercion. – Shepmaster Jan 09 '19 at 22:16
0

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:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Note that you also wouldn't be able to call a method accepting a slice by immutable reference on an array. It doesn't really matter whether the receiver is defined as `&self` or `&mut self` – neither of them would work. – Sven Marnach Jan 09 '19 at 22:06
  • @SvenMarnach yep, I lost the important word "reference" there. – Shepmaster Jan 09 '19 at 22:11