How would i go about making this work for async? impl<S: Read + Write> Client<S>

How would i go about making this work for async? in the least amount of lines possible

use anyhow::Result;
use std::io::{Read, Write};

mod types;

pub struct Client<S>(pub S);

impl<S: Read + Write> Client<S> {
    pub fn write(&mut self, buffer: &[u8], ptype: u8) -> Result<()> {
        let len = &(buffer.len() as u32).to_be_bytes();
        self.0.write_all(len)?;
        self.0.write_all(&[ptype])?;
        self.0.write_all(buffer)?;
        self.0.flush()?;
        Ok(())
    }

    pub fn read(&mut self) -> Result<(u8, Vec<u8>)> {
        let mut len_buf = [0; 4];
        self.0.read_exact(&mut len_buf)?;
        let len = u32::from_be_bytes(len_buf) as usize;
        let mut ptype_buf = [0; 1];
        self.0.read_exact(&mut ptype_buf)?;
        let ptype = ptype_buf[0];
        let mut buffer = vec![0; len];
        self.0.read_exact(&mut buffer)?;
        Ok((ptype, buffer))
    }
}

the short anwser:

for reading, add async keyword to your function, replace std::io::Read with futures_util::io::AsyncReadExt, and add .await to read() methods, something like:

    pub async fn read(&mut self) -> Result<(u8, Vec<u8>)>
    where
        S: AsyncReadExt + Unpin
    {
        let mut len_buf = [0; 4];
        self.0.read_exact(&mut len_buf).await?;
        let len = u32::from_be_bytes(len_buf) as usize;
        let mut ptype_buf = [0; 1];
        self.0.read_exact(&mut ptype_buf).await?;
        let ptype = ptype_buf[0];
        let mut buffer = vec![0; len];
        self.0.read_exact(&mut buffer).await?;
        Ok((ptype, buffer))
    }

for writing, it's similar, but with AsyncWriteExt.

the long answer:

it depends what async runtime you are using. for example, if you are using tokio only, you can use tokio::io::AsyncReadExt and tokio::io::AsyncWriteExt. if you are using async_std, you can use async_std::io::ReadExt and async_std::io::WriteExt etc.

the futures_io in theory should be supported by different async ecosystems, but you might need a compatibility layer, e.g. for tokio, you can use tokio_util with the "compat" feature.

the Unpin bound is necessary due to how async and Future work in rust. if you know how futures work, technically you can use futures::future::poll_fn() and the AsyncRead/AsyncWrite trait directly (no Ext, no Unpin), but you need to manage the pinning yourself, so I would not recommend it if you are new to async rust.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.