I have a question about byteorder crate and array slices. In the following code parse_stream function is working properly:
fn parse_stream(input_stream : &[u8]) -> Result<StreamData, std::io::Error> {
...
// Slice incoming stream into an interesting part
let mut stream = &input_stream [start..start+length]; // (1)
// Do some reads
let input1 = stream.read_u32::<LittleEndian>()?;
let input2 = stream.read_u32::<LittleEndian>()?;
let input3 = stream.read_u32::<LittleEndian>()?;
...
// Borrow mutable u8 slice for further parsing
parse_table(&mut stream);
...
}
fn parse_table(stream : &mut [u8]) -> Option<Table> {
let input = stream.read_u32::<LittleEndian>().unwrap(); // (2)
...
}
But passing stream down to another function generates compilation error at line (2):
error[E0599]: no method named `read_u32` found for type `&mut [u8]` in the current scope
--> src\metadata\root.rs:166:33
|
166 | let input = stream.read_32::<LittleEndian>().unwrap();
| ^^^^^^^^
|
= note: the method `read_u32` exists but the following trait bounds were not satisfied:
`&mut [u8] : byteorder::ReadBytesExt`
`[u8] : byteorder::ReadBytesExt`
How to interpret and fix it? Why read_u32 works in parse_stream on mutable &[u8] and does not work on &mut [u8] in parse_table? I'm probably missing something obvious here...
The byteorder::ReadBytesExt trait is implemented for every type that has std::io::Read implemented for it. std::io::Read is implemented for &[u8] but not &mut [u8], which you can see here in the "implementors" section: Read in std::io - Rust
I guess if stream is a &mut [u8], then you should be able to re-borrow it as a &[u8] via &*stream. Or perhaps by wrapping it in a std::io::Cursor.
This impl is indeed tricky, and confused me a lot when I first saw it.
The key here is to realize that all the Read::read_* methods (which byteorder calls) operate on a &mut T, i.e. in this case a &mut &[u8]. This allows them to change where your slice is pointing at.
You can imagine the slice as being a struct (ignoring the lifetime tracked by the compiler)
Since Read::read gets a &mut Slice, it can update the addr and size members after advancing a certain amount.
To answer your final point: the binding has to be mut since where stream points to has changed fter reading. The slice itself doesn't since nobody is changing the u8 data it points to.