External Publication
Visit Post

Going from AsyncWrite to AsyncRead

Rust Internals [Unofficial] April 4, 2026
Source

futures-util::io::copy provides a efficient way to write a AsyncRead into a Asyncwrite. I recently found myself in need for the other way around: I need the AsyncWrite interface to expose a AsyncRead.

Well not exactly. I needed a way to go from AsyncWrite to Stream in axum to Stream big Zip files. The corresponding issue on github sparked interest from the axum developers, so I implemented a first draft. The usage currently looks like:

// Stream implements: Stream<Item=Result<Bytes>>
let stream = Stream::new(|w: Writer| async move {
    let mut w = std::pin::pin!(w);
    write_all(&mut w, &[42]).await?;
    write_all(w, &[42]).await
});

Even if I still think, that this would be very valuable in the axum project, the abstraction-level feels wrong. The implementation doesn't use any axum related code, just Stream and Bytes, so it could be used on the client side too. Imo, this fundamental building-block shouldn't be in a small standalone project, as this contributes to projects having thousands of small dependencies, which is making supply-chain security a nightmare. I struggled to find a project where it would fit.

I then asked myself, if the official futures-util crate could add the difficult (unsafe) part of this conversion, by providing a AsyncRead from a Closure, which gets a AsyncWrite and returns a Future<Output=std::io::Result<()>> when it finishes. This would fit, as it doesn't introduce any dependencies and it is a zero-cost abstraction without any intermediate buffers.

Discussion in the ATmosphere

Loading comments...