Skip to content

Can we simplify io::copy? #365

Closed
Closed
@ghost

Description

The current signature of async_std::io::copy is:

pub async fn copy<R, W>(reader: &mut R, writer: &mut W) -> io::Result<u64>
where
    R: Read + Unpin + ?Sized,
    W: Write + Unpin + ?Sized;

Both reader and writer need to be mutable references because we're just mimicking std::io::copy.

Unfortunately, this API is annoying to use when we want to share TcpStreams. Just look at this convoluted &mut (&stream, &stream) pattern:

async fn process(stream: TcpStream) -> io::Result<()> {
    let (reader, writer) = &mut (&stream, &stream);
    io::copy(reader, writer).await?;
    Ok(())
}

I think we can do better. What if we didn't follow the API from std and had the following instead?

pub async fn copy<R, W>(reader: R, writer: W) -> io::Result<u64>
where
    R: Read + Unpin,
    W: Write + Unpin;

This might look like a less powerful APIs than the previous one, but I believe it is functionally the same. Note that we have these blanket impls of Read and Write:

impl<T: Read + Unpin + ?Sized> Read for &mut T {}
impl<T: Write + Unpin + ?Sized> Write for &mut T {}

That means if we can pass &mut T into the previous API, then it should be totally fine to pass it into the new one too! In other words, the new API is fully compatible with the previous one (unless I'm missing something here).

So the cool thing is that while it might seem we're deviating from the std APIs, we kind of aren't. :)

Here's how we can write an echo TCP server using the new async_std::io::copy:

async fn process(stream: TcpStream) -> io::Result<()> {
    io::copy(&stream, &stream).await?;
    Ok(())
}

And here's how we do it on Arc<TcpStream>:

async fn process(stream: Arc<TcpStream>) -> io::Result<()> {
    io::copy(&*stream, &*stream).await?;
    Ok(())
}

This change could make sharing streams a lot easier, and we've seen plenty of people struggle with this problem. Wdyt?

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions