Cannot use tokio::io::{AsyncReadExt} and byteorder::{ReadBytesExt} together inside one file

I have some Reader struct that use tokio for read tcp data from server. I want to implement a function inside Reader that use byteorder crate, but I have an error because tokio::io::{AsyncReadExt} overlaps with byteorder::{ReadBytesExt}.

This is my code:

// main function of Reader
pub async fn read(&mut self) -> Result<Vec<Vec<u8>>, Error> {
        let mut buffer = [0u8; 16384];

        match self.stream.read(&mut buffer).await {
            Ok(bytes_count) => {
                let result = match self.decryptor.as_mut() {
                    Some(decryptor) => {
                        Self::parse_packets(buffer[..bytes_count].to_vec(), decryptor)
                    },
                    _ => {
                        vec![buffer[..bytes_count].to_vec()]
                    },
                };

                Ok(result)
            },
            Err(err) => {
                // ... process errors
            },
        }
    }

// the function that use byteorder crate
fn parse_packets(raw_data: Vec<u8>, decryptor: &mut Decryptor) -> Vec<Vec<u8>> {
        let mut reader = Cursor::new(&raw_data);

        let mut packets = Vec::new();
        while reader.position() < (raw_data.len() as u64) {
            let mut header = [0u8; INCOMING_HEADER_LENGTH as usize];
            reader.read_exact(&mut header).unwrap();

            let mut header = Cursor::new(decryptor.decrypt(&header.to_vec()));
            let size = header.read_u16::<BigEndian>().unwrap();
            let opcode = header.read_u16::<LittleEndian>().unwrap();

            // ... generate packet
            packets.push(packet);
        }

        packets
    }

So, currently I implemented trait to which I moved parse_packets function. But it seems overkill to have a trait which will be used only once. Could somebody explain how to use both inside one file ?

Do you mean you are trying to implement both traits? Or you have problems calling functions from the traits?

For ambiguous trait methods you can use the syntax as described better here: Disambiguating overlapping traits - Rust By Example

2 Likes

could you tell, how to use this with method ? Considering I need to apply ReadBytesExt for both:

let mut header = Cursor::new(decryptor.decrypt(&header.to_vec()));
// need to apply here
let size = header.read_u16::<BigEndian>().unwrap();
let opcode = header.read_u16::<LittleEndian>().unwrap();

The byteorder traits are not async, so you can't use them with an async IO resource. However, Tokio's AsyncReadExt trait actually already has the methods necessary to perform those reads:

header.read_u16().await?; // big-endian
header.read_u16_le().await?; // little-endian

You can find the documentation for the method at AsyncReadExt::read_u16.

I tried to use approach to deal with traits overlap, but still have an error:

error[E0038]: the trait `ReadBytesExt` cannot be made into an object
    --> src\network\stream.rs:74:13
     |
74   |             ReadBytesExt::read_exact(&mut reader, &mut body).expect(
     |             ^^^^^^^^^^^^ `ReadBytesExt` cannot be made into an object

112  |     fn read_u16<T: ByteOrder>(&mut self) -> Result<u16> {
     |        ^^^^^^^^ the trait cannot be made into an object because method `read_u16` has generic type parameters
...
139  |     fn read_i16<T: ByteOrder>(&mut self) -> Result<i16> {
     |        ^^^^^^^^ the trait cannot be made into an object because method `read_i16` has generic type parameters
...
165  |     fn read_u24<T: ByteOrder>(&mut self) -> Result<u32> {
     |        ^^^^^^^^ the trait cannot be made into an object because method `read_u24` has generic type parameters
...
191  |     fn read_i24<T: ByteOrder>(&mut self) -> Result<i32> {
     |        ^^^^^^^^ the trait cannot be made into an object because method `read_i24` has generic type parameters
...
217  |     fn read_u32<T: ByteOrder>(&mut self) -> Result<u32> {
     |        ^^^^^^^^ the trait cannot be made into an object because method `read_u32` has generic type parameters
...
243  |     fn read_i32<T: ByteOrder>(&mut self) -> Result<i32> {
     |        ^^^^^^^^ the trait cannot be made into an object because method `read_i32` has generic type parameters
...
269  |     fn read_u48<T: ByteOrder>(&mut self) -> Result<u64> {
     |        ^^^^^^^^ the trait cannot be made into an object because method `read_u48` has generic type parameters
...
295  |     fn read_i48<T: ByteOrder>(&mut self) -> Result<i64> {
     |        ^^^^^^^^ the trait cannot be made into an object because method `read_i48` has generic type parameters
...
321  |     fn read_u64<T: ByteOrder>(&mut self) -> Result<u64> {
     |        ^^^^^^^^ the trait cannot be made into an object because method `read_u64` has generic type parameters
...
347  |     fn read_i64<T: ByteOrder>(&mut self) -> Result<i64> {
     |        ^^^^^^^^ the trait cannot be made into an object because method `read_i64` has generic type parameters
...
376  |     fn read_u128<T: ByteOrder>(&mut self) -> Result<u128> {
     |        ^^^^^^^^^ the trait cannot be made into an object because method `read_u128` has generic type parameters
...
402  |     fn read_i128<T: ByteOrder>(&mut self) -> Result<i128> {
     |        ^^^^^^^^^ the trait cannot be made into an object because method `read_i128` has generic type parameters
...
427  |     fn read_uint<T: ByteOrder>(&mut self, nbytes: usize) -> Result<u64> {
     |        ^^^^^^^^^ the trait cannot be made into an object because method `read_uint` has generic type parameters
...
452  |     fn read_int<T: ByteOrder>(&mut self, nbytes: usize) -> Result<i64> {
     |        ^^^^^^^^ the trait cannot be made into an object because method `read_int` has generic type parameters
...
460  |     fn read_uint128<T: ByteOrder>(&mut self, nbytes: usize) -> Result<u128> {
     |        ^^^^^^^^^^^^ the trait cannot be made into an object because method `read_uint128` has generic type parameters
...
468  |     fn read_int128<T: ByteOrder>(&mut self, nbytes: usize) -> Result<i128> {
     |        ^^^^^^^^^^^ the trait cannot be made into an object because method `read_int128` has generic type parameters
...
499  |     fn read_f32<T: ByteOrder>(&mut self) -> Result<f32> {
     |        ^^^^^^^^ the trait cannot be made into an object because method `read_f32` has generic type parameters
...
530  |     fn read_f64<T: ByteOrder>(&mut self) -> Result<f64> {
     |        ^^^^^^^^ the trait cannot be made into an object because method `read_f64` has generic type parameters

this is code I try to use:

fn parse_packets(raw_data: Vec<u8>, decryptor: &mut Decryptor) -> Vec<Vec<u8>> {
        let mut reader = Cursor::new(&raw_data);

        let mut packets = Vec::new();
        while ReadBytesExt::position(&mut reader) < (raw_data.len() as u64) {
            let mut header = [0u8; INCOMING_HEADER_LENGTH as usize];
            ReadBytesExt::read_exact(&mut reader, &mut header).unwrap();

            let mut header = Cursor::new(decryptor.decrypt(&header.to_vec()));
            let size = ReadBytesExt::read_u16::<BigEndian>(&mut header).unwrap();
            let opcode = ReadBytesExt::read_u16::<LittleEndian>(&mut header).unwrap();

            let mut body = vec![0u8; (size - INCOMING_OPCODE_LENGTH) as usize];
            ReadBytesExt::read_exact(&mut reader, &mut body).expect(
                &format!("Cannot read raw data for opcode {} and size {}", opcode, size)
            );

            // ... rest code
        }

        packets
    }

does it still possible to fix ?

the data I pass to cursor there is just Vec, so it can be used with cursor and byteorder. I use AsyncReadExt there only for TcpStream. The data I got from tcp stream I pass then to parser.

The read_exact method is not defined on ReadBytesExt.

1 Like

If you can provide a playground example it would be easier to see the scopes at play.

But looking at the example linked earlier:

let size = ReadBytesExt::read_u16::<BigEndian>(&mut header).unwrap();

Should probably be:

let size = <header as ReadBytesExt>.read_u16::<BigEndian>().unwrap();
1 Like

Finally, I fixed the code using your solution. So, for read_exact, as @alice mentioned, I should use std::io::Read instead of ReadBytesExt. For read_u16 and so on I should use ReadBytesExt.

Thanks a lot for help and detailed explanation !