xwt icon indicating copy to clipboard operation
xwt copied to clipboard

tokio::io::AsyncRead/Write for xwt-wtransport

Open xangelix opened this issue 1 year ago • 2 comments

Forgive me if there's an ergonomic way to do this that I'm simply not understanding (because I'm sure this library is quite thoughtfully crafted! and I'm not well enough read on Rust's stream traits), but is there a reason that the tokio AsyncRead and AsyncWrite traits are not part of xwt_core?

I was looking to do something like this:

pub async fn proc_trx<S: xwt_core::base::Session>(
    mut send_stream: S::SendStream,
    mut recv_stream: S::RecvStream,
)
where
    S::SendStream: tokio::io::AsyncWrite + Unpin,
    S::RecvStream: tokio::io::AsyncRead + Unpin,
{

However xwt-wtransport doesn't support the traits directly, only the inner values for SendStream and RecvStream do (from the wtransport crate.

xwt_web already implements these traits-- so is there any reason the inner struct's support isn't mapped to the outer struct for wtransport as well? Is it just because you would have to use unsafe code to re-pin the inner value?

e.g.

let inner_pin: std::pin::Pin<&mut wtransport::SendStream> = unsafe { self.map_unchecked_mut(|s| &mut s.0) };

for

impl tokio::io::AsyncWrite for SendStream {
    fn poll_write(
        self: std::pin::Pin<&mut Self>,
        cx: &mut std::task::Context<'_>,
        buf: &[u8],
    ) -> std::task::Poll<Result<usize, std::io::Error>> {
        let inner_pin: std::pin::Pin<&mut wtransport::SendStream> =
            unsafe { self.map_unchecked_mut(|s| &mut s.0) };
        inner_pin.poll_write(cx, buf)
    }

    fn poll_flush(
        self: std::pin::Pin<&mut Self>,
        cx: &mut std::task::Context<'_>,
    ) -> std::task::Poll<Result<(), std::io::Error>> {
        let inner_pin: std::pin::Pin<&mut wtransport::SendStream> =
            unsafe { self.map_unchecked_mut(|s| &mut s.0) };
        inner_pin.poll_flush(cx)
    }

    fn poll_shutdown(
        self: std::pin::Pin<&mut Self>,
        cx: &mut std::task::Context<'_>,
    ) -> std::task::Poll<Result<(), std::io::Error>> {
        let inner_pin: std::pin::Pin<&mut wtransport::SendStream> =
            unsafe { self.map_unchecked_mut(|s| &mut s.0) };
        inner_pin.poll_shutdown(cx)
    }
}

impl tokio::io::AsyncRead for RecvStream {
    fn poll_read(
        self: std::pin::Pin<&mut Self>,
        cx: &mut std::task::Context<'_>,
        buf: &mut tokio::io::ReadBuf<'_>,
    ) -> std::task::Poll<std::io::Result<()>> {
        let inner_pin: std::pin::Pin<&mut wtransport::RecvStream> =
            unsafe { self.map_unchecked_mut(|s| &mut s.0) };
        inner_pin.poll_read(cx, buf)
    }
}

Or is this a bad idea for some other reason?

Thanks!

xangelix avatar Apr 14 '25 17:04 xangelix

I just didn't add them - mainly due to lack of need on my side; a PR is welcome :D

There is no reason why not to add them.

Implementing tokio traits was always more of an experiment, so I didn't do it too much.

That said, I'd use pin project lite instead of manually written unsafe.

MOZGIII avatar Apr 15 '25 02:04 MOZGIII

Okay cool--I submitted a PR! 😁

xangelix avatar Apr 15 '25 14:04 xangelix

Closed with https://github.com/MOZGIII/xwt/pull/214

MOZGIII avatar Sep 23 '25 19:09 MOZGIII