This example is part of a larger repository of examples, async-applied.
reqwest is an excellent crate for making HTTP requests in the vein of wget, curl, etc. tokio is the de facto Rust async runtime, especially for io-driven tasks. This example builds on the reqwest-tokio example. In that example we wondered why we could not call tokio::io::copy
to copy the body of the reqwest::Response
into outfile
?
For reasons related to good API design a request::Response
does not implement AsyncRead
directly. It is, however, possible to convert it to a futures::io::Stream
using bytes_stream()
. A Stream
is naught but an async iterator, analagous to how we called while let Some(chunk) = download.chunk().await?
in the prior example. It is therefore not surprising that there is an stream extensions trait that allows us to go from a stream to a futures::io::AsyncRead
. Note that, because AsyncRead
uses futures::io::Error
, we must map from request::Error
in the process.
Having turned our reqwest::Response
into an AsyncRead
it should now be straightforward to invoke tokio::io::copy
... but it is not. For reasons described here and here, futures::io::AsyncRead
is not compatible with tokio::io::AsyncRead
. Bummer. Fortunately there is a compatibility layer between tokio and futures!
The compatibility layer is found in a separate crate, tokio-util. To use it we pull in the appropriate extension trait:
use tokio_util::compat::FuturesAsyncReadCompatExt;
And then simply call compat()
in the futures::io::AsyncRead
to make it into a tokio::io::AsyncRead
. Although it is annoying that futures and tokio have chosen not to use the same traits (at least for now), converting from one to the other really is not that hard.
Note: To use the compatibility layer you will need to use tokio-util version 0.3 or greater in your
Cargo.toml
.