I am trying to write a test program with tokio that grabs a file from a website and writes the streamed response to a file. The hyper website shows an example that uses a while loop and uses the .data() method the response body, but I'd like to manipulate the stream with .map() and a couple others.
I thought the next reasonable thing to try would be to convert the stream to an AsyncRead by using the .into_async_read() method from TryStreamExt, but that doesn't seem to work. I had to use a map to convert the hyper::error::Error into a std::error::Error to get a TryStream, but now the compiler is telling me that AsyncRead isn't implemented for the transformed stream. Here is my main.rs file and the error:
use std::error::Error;
use futures::stream::{StreamExt, TryStreamExt};
use http::Request;
use hyper::{Body, Client};
use hyper_tls::HttpsConnector;
use tokio::fs::File;
use tokio::io;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let https = HttpsConnector::new();
let client = Client::builder().build::<_, Body>(https);
let request = Request::get("some file from the internet").body(Body::empty())?;
let response = client.request(request).await?;
let mut stream = response
.body()
.map(|result| result.map_err(|error| std::io::Error::new(std::io::ErrorKind::Other, "Error!")))
.into_async_read();
let mut file = File::create("output file").await?;
io::copy(&mut stream, &mut file).await?;
Ok(())
}
error[E0277]: the trait bound `futures_util::stream::try_stream::into_async_read::IntoAsyncRead<futures_util::stream::stream::map::Map<hyper::body::body::Body, [closure#src/main.rs:20:14: 20:103]>>: tokio::io::async_read::AsyncRead` is not satisfied
--> src/main.rs:24:5
|
24 | io::copy(&mut stream, &mut file).await?;
| ^^^^^^^^ the trait `tokio::io::async_read::AsyncRead` is not implemented for `futures_util::stream::try_stream::into_async_read::IntoAsyncRead<futures_util::stream::stream::map::Map<hyper::body::body::Body, [closure#src/main.rs:20:14: 20:103]>>`
|
::: /Users/jackson/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.13/src/io/util/copy.rs:63:12
|
63 | R: AsyncRead + Unpin + ?Sized,
| --------- required by this bound in `tokio::io::util::copy::copy`
error[E0277]: the trait bound `futures_util::stream::try_stream::into_async_read::IntoAsyncRead<futures_util::stream::stream::map::Map<hyper::body::body::Body, [closure#src/main.rs:20:14: 20:103]>>: tokio::io::async_read::AsyncRead` is not satisfied
--> src/main.rs:24:5
|
24 | io::copy(&mut stream, &mut file).await?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `tokio::io::async_read::AsyncRead` is not implemented for `futures_util::stream::try_stream::into_async_read::IntoAsyncRead<futures_util::stream::stream::map::Map<hyper::body::body::Body, [closure#src/main.rs:20:14: 20:103]>>`
|
= note: required because of the requirements on the impl of `core::future::future::Future` for `tokio::io::util::copy::Copy<'_, futures_util::stream::try_stream::into_async_read::IntoAsyncRead<futures_util::stream::stream::map::Map<hyper::body::body::Body, [closure#src/main.rs:20:14: 20:103]>>, tokio::fs::file::File>`
You almost had it. You invoked into_async_read which gives you an implementation of futures::io::AsyncRead but you want a tokio::io::AsyncRead.
The tokio-util crate gives you a tool to do this conversion.
Add to your Cargo.toml:
tokio-util = { version = "0.3.1", features=["compat"] }
And say you add a conversion function like this:
fn to_tokio_async_read(r: impl futures::io::AsyncRead) -> impl tokio::io::AsyncRead {
tokio_util::compat::FuturesAsyncReadCompatExt::compat(r)
}
then your code could become:
let mut futures_io_async_read = response
.body()
.map(|result| result.map_err(|error| std::io::Error::new(std::io::ErrorKind::Other, "Error!")))
.into_async_read();
let tokio_async_read = to_tokio_async_read(futures_io_async_read)
let mut file = File::create("output file").await?;
io::copy(&mut tokio_async_read, &mut file).await?;
Related
In the documentation for Mutex, it says that it implements Send and Sync -- which makes sense, because a Mutex is designed to be accessed from multiple threads that are locking, using the resource it protects, then unlocking.
However, in my code below, I get a compiler error that, as far as I can tell, complains that the Mutex doesn't implement Send/Sync:
error[E0599]: the method `try_init` exists for struct `SubscriberBuilder<DefaultFields, Format, tracing::level_filters::LevelFilter, std::sync::Mutex<MultiWriter>>`, but its trait bounds were not satisfied
--> src/main.rs:131:10
|
131 | .try_init().expect("setting default subscriber failed");
| ^^^^^^^^ method cannot be called on `SubscriberBuilder<DefaultFields, Format, tracing::level_filters::LevelFilter, std::sync::Mutex<MultiWriter>>` due to unsatisfied trait bounds
|
::: /Users/sean/.cargo/registry/src/github.com-1ecc6299db9ec823/tracing-subscriber-0.3.16/src/fmt/fmt_layer.rs:62:1
|
62 | / pub struct Layer<
63 | | S,
64 | | N = format::DefaultFields,
65 | | E = format::Format<format::Full>,
66 | | W = fn() -> io::Stdout,
67 | | > {
| | -
| | |
| |_doesn't satisfy `_: std::marker::Send`
| doesn't satisfy `_: std::marker::Sync`
|
= note: the following trait bounds were not satisfied:
`tracing_subscriber::fmt::Layer<Registry, DefaultFields, Format, std::sync::Mutex<MultiWriter>>: std::marker::Send`
`tracing_subscriber::fmt::Layer<Registry, DefaultFields, Format, std::sync::Mutex<MultiWriter>>: std::marker::Sync`
If I remove the line .with_writer(mw) from my code below, the error goes away. Clearly the problem is related to the writer, but I'm not sure how to do this correctly.
The goal of the code is to write the logs from the tracing framework to both stderr and a file specified from dotenvy if a file name is specified (it's optional).
NB: I'm using the latest stable Rust and the released version of each crate used below, and compiling with std, libc, alloc, etc. (full Rust, not embedded) on MacOS, but the code is expected to work on the "multi-platform x86(_64) desktop" environment (Windows/MacOS/desktop Linux). For Tokio I have features = ["full"].
use std::fs::File;
use std::io::Write;
use std::sync::Mutex;
use dotenvy::var;
se std::sync::Arc;
use tracing::Level;
struct MultiWriter {
writers: Vec<Arc<dyn Write>>,
}
impl Write for MultiWriter {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
for writer in self.writers.iter_mut() {
writer.write(buf)?;
}
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
for writer in self.writers.iter_mut() {
writer.flush()?;
}
Ok(())
}
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let mut writers: Vec<Arc<dyn Write>> = vec![(Arc::new(std::io::stderr()))];
if let Some(log_file) = var("log_file").ok() {
writers.push(Arc::new(File::create(log_file).unwrap()));
}
let mw = Mutex::new(MultiWriter { writers });
let tsb = tracing_subscriber::FmtSubscriber::builder()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env()).with_ansi(false)
.with_writer(mw);
if let Ok(log_level) = var("log_level") {
match log_level.to_uppercase().as_str() {
"TRACE" => tsb.with_max_level(Level::TRACE),
"DEBUG" => tsb.with_max_level(Level::DEBUG),
"INFO" => tsb.with_max_level(Level::INFO),
"WARN" => tsb.with_max_level(Level::WARN),
"ERROR" => tsb.with_max_level(Level::ERROR),
_ => tsb.with_max_level(Level::INFO)
}
.try_init().expect("setting default subscriber failed");
}
}
In the documentation for Mutex, it says that it implements Send and Sync
That's not completely true:
impl<T: ?Sized + Send> Send for Mutex<T>
impl<T: ?Sized + Send> Sync for Mutex<T>
This means that a Mutex is Send and Sync only if T is Send (the reason for this is described in this question.
However, T isn't Send here:
T is a struct MultiWriter
struct MultiWriter contains a dyn Write
dyn Write is not Send (at least not always)
in turn, struct MultiWriter isn't either.
To fix this, replace dyn Write by dyn Write + Send, and it should work.
I think it needs to be emphasized that the OP is doing Mutex<Arc<MyType>> that this is different than Arc<Mutex<MyType>>. If a type is Send and wrapped in a Mutex, the whole thing is Send + Sync, but since Arc needs both, that's important here. But if Mutex is on the outside (like it is here) then Arc isn't satisfied by what's inside of it.
As Elias cites (correctly), Mutex only requires it's contained value to be Send, not both Send + Sync. The OP's problem (one of them) is that Mutex is on the outside, and Arc demands both Sync + Send which dyn Write isn't.
Arc:
#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<T: ?Sized + Sync + Send> Send for Arc<T> {}
#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<T: ?Sized + Sync + Send> Sync for Arc<T> {}
Mutex:
#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<T: ?Sized + Send> Send for Mutex<T> {}
#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}
Ultimately #ChayimFriedman is right that for the OP's block of code, they need both (dyn + Write + Send + Sync), but I wanted to put down here even more as to why.
I'm currently working with the 'sha2' library.
One of the functions in my project is supposed to calculate the hash of a file using
different methods (Sha224, Sha256, Sha384, Sha512).
All of the hash methods use the trait sha2::Digest and
all return a GenericArray<u8, Self::OutputSize>.
However, since a generic type is used, i get following error message:
cannot add `<T as OutputSizeUser>::OutputSize` to `<T as OutputSizeUser>::OutputSize`
with this code:
use sha2::{Digest, Sha224, Sha256, Sha384, Sha512};
use std::error::Error;
use std::{env, fs, io, process};
//...
impl AutoSha {
//...
fn calc_hash<T: Digest + io::Write>(&self, file_path: String) -> Result<String, Box<dyn Error>> {
let mut hasher = T::new();
let mut file = fs::File::open(file_path)?;
io::copy(&mut file, &mut hasher)?;
let hash = hasher.finalize();
Ok(format!("{:x}", hash)) // This results in the error message
}
}
//Call example:
let auto_sha: AutoSha = AutoSha::new();
let hash = auto_sha.calc_hash::<Sha256>("path/to/file".to_string());
Using, for example, Sha256::new() instead of T::new() works fine.
I have tried to set both 'OutputSizeUser' and 'OutputSize' as trait bounds but was unable
to find what namespace they are located in.
According to the documentation it is supposedly part of 'crypto_common' which should be included in the project as it is a dependency of 'sha2', but i was not able to include it.
Additionally, i do not know whether that would solve my issue either.
How can i format the return value of 'hasher.finalize()' to a hexadecimal string?
Full error log
error[E0277]: cannot add `<T as OutputSizeUser>::OutputSize` to `<T as OutputSizeUser>::OutputSize`
--> src/main.rs:80:28
|
80 | Ok(format!("{:x}", hash))
| ^^^^ no implementation for `<T as OutputSizeUser>::OutputSize + <T as OutputSizeUser>::OutputSize`
|
= help: the trait `Add` is not implemented for `<T as OutputSizeUser>::OutputSize`
= note: required because of the requirements on the impl of `LowerHex` for `GenericArray<u8, <T as OutputSizeUser>::OutputSize>`
note: required by a bound in `ArgumentV1::<'a>::new_lower_hex`
= note: this error originates in the macro `$crate::__export::format_args` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider further restricting the associated type
|
73 | fn calc_hash<T: Digest + io::Write>(&self, file_path: String) -> Result<String, Box<dyn Error>> where <T as OutputSizeUser>::OutputSize: Add {
| ++++++++++++++++++++++++++++++++++++++++++++
For more information about this error, try `rustc --explain E0277`.
error: could not compile `hash-cmp` due to previous error
I have tried to use the recommended solution with std::ops::Add but did not manage to get it to work either.
Useful links
sha2 Documentation
Full source code (Github)
After experimenting/reading for a while i found following solution:
fn calc_hash<T: Digest + io::Write>(&self, file_path: String) -> Result<String, Box<dyn Error>> {
let mut hasher = T::new();
let mut file = fs::File::open(file_path)?;
io::copy(&mut file, &mut hasher)?;
let hash = hasher.finalize();
let mut hash_string = "".to_string();
for byte in hash.iter() {
hash_string = format!("{}{:02x}", hash_string, byte);
}
Ok(hash_string)
}
I'm basically just iterating over each byte of the GenericArray and then concat it with the end result.
Link to playground
I am trying to implement a custom data format with serde, I've been struggling with the deserialize_str method
pub struct Deserializer<R> {
rdr: R,
}
impl<'de, 'a, R: io::Read + 'de> de::Deserializer<'de> for &'a mut Deserializer<R> {
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let len = self.read_i16()?; // implemention below
if len == 0 || len == -1 {
return visitor.visit_borrowed_str("");
}
let len = len as usize;
let buf = self.read_exact(len)?; // implemention below
let out_str = std::str::from_utf8(&buf)?;
// visitor.visit_borrowed_str(out_str) doesn't compile
visitor.visit_str(out_str) // compiles but errors
}
}
impl<R: io::Read> Deserializer<R> {
fn read_exact(&mut self, len: usize) -> Result<Vec<u8>> {
let mut buf = vec![0; len];
self.rdr.read_exact(&mut buf)?;
Ok(buf)
}
fn read_i16(&mut self) -> io::Result<i8> {
self.rdr.read_i16::<byteorder::NetworkEndian>()
}
}
When using visitor.visit_borrowed_str(out_str), I get the error
|
94 | impl<'de, 'a, R: io::Read + 'de> de::Deserializer<'de> for &'a mut Deserializer<R> {
| --- lifetime `'de` defined here
...
149 | let out_str = std::str::from_utf8(&buf)?;
| ^^^^ borrowed value does not live long enough
150 |
151 | visitor.visit_borrowed_str(out_str)
| ----------------------------------- argument requires that `buf` is borrowed for `'de`
152 | }
| - `buf` dropped here while still borrowed
I understand that out_str needs to somehow live longer than its scope, but I can't find a way to go about it.
To use visit_borrowed_str, you need to hand it a reference to something that lives as long as your deserializer. Creating a new temporary Vec with read_exact won't do, you need to get access to the underlying slice, e.g. std::str::from_utf8(self.rdr.get_ref()[self.rdr.position()..][..len]) or similar. If you want to keep R a generic std::io::Read, I think you can't use visit_borrowed_str. serde_json e.g. handles this by having a special Read that returns a reference to the underlying data if it can, and then only uses visit_borrowed_str if it does have a reference to the underlying data.
Also, if you ask a deserializer to deserialize to a borrowed string when it can't, it must necessarily error. That holds true for serde_json as well. So the error from visit_str is not an error in your deserializer implementation, but an error in how you use the deserializer. You should have asked to deserialize to a String or Cow<str> instead (not that your serializer could ever give you a Cow::Borrowed, but asking for a &str just isn't a good idea with any deserializer, asking for a Cow<str> is the thing generally recommended instead).
I want to leverage Tokio's runtime to handle a variable amount of async futures. Since the count of futures is unknown at compile time, it seems FuturesUnordered is my best option (macros such as select! require specifying your branches at compile time; join_all might be possible but the docs recommend FuturesUnordered "in a lot of cases" when order doesn't matter).
The logic of this snippet is a recv() loop getting pushed to the bucket of futures, which should always run. When new data arrives, its parsing/processing gets pushed to the futures bucket too (instead of being processed immediately). This ensures the receiver maintains low latency in responding to new events, and data processing (potentially computationally expensive decryption) occurs concurrently with all other data processing async blocks (plus the listening receiver).
This thread explains why the futures get .boxed(), by the way.
The problem is this cryptic error:
error[E0277]: `dyn futures::Future<Output = ()> + std::marker::Send` cannot be shared between threads safely
--> src/main.rs:27:8
|
27 | }).boxed());
| ^^^^^ `dyn futures::Future<Output = ()> + std::marker::Send` cannot be shared between threads safely
|
= help: the trait `Sync` is not implemented for `dyn futures::Future<Output = ()> + std::marker::Send`
= note: required because of the requirements on the impl of `Sync` for `Unique<dyn futures::Future<Output = ()> + std::marker::Send>`
= note: required because it appears within the type `Box<dyn futures::Future<Output = ()> + std::marker::Send>`
= note: required because it appears within the type `Pin<Box<dyn futures::Future<Output = ()> + std::marker::Send>>`
= note: required because of the requirements on the impl of `Sync` for `FuturesUnordered<Pin<Box<dyn futures::Future<Output = ()> + std::marker::Send>>>`
= note: required because of the requirements on the impl of `std::marker::Send` for `&FuturesUnordered<Pin<Box<dyn futures::Future<Output = ()> + std::marker::Send>>>`
= note: required because it appears within the type `[static generator#src/main.rs:16:25: 27:6 _]`
= note: required because it appears within the type `from_generator::GenFuture<[static generator#src/main.rs:16:25: 27:6 _]>`
= note: required because it appears within the type `impl futures::Future`
It looks like pushing to an UnorderedFutures "recursively" (not really I guess, but what else would you call it?) doesn't work, but I'm not sure why. This error indicates some Sync trait requirement isn't met for the Box'd & Pin'd async blocks being tended to by the FuturesUnordered -- a requirement I guess is only imposed because &FuturesUnordered (used during futures.push(...) because that method borrows &self) needs it for its Send trait... or something?
use std::error::Error;
use tokio::sync::mpsc::{self, Receiver, Sender};
use futures::stream::futures_unordered::FuturesUnordered;
use futures::FutureExt;
#[tokio::main]
pub async fn main() -> Result<(), Box<dyn Error>> {
let mut futures = FuturesUnordered::new();
let (tx, rx) = mpsc::channel(32);
tokio::spawn( foo(tx) ); // Only the receiver is relevant; its transmitter is
// elsewhere, occasionally sending data.
futures.push((async { // <--- NOTE: futures.push()
loop {
match rx.recv().await {
Some(data) => {
futures.push((async move { // <--- NOTE: nested futures.push()
let _ = data; // TODO: replace with code that processes 'data'
}).boxed());
},
None => {}
}
}
}).boxed());
while let Some(_) = futures.next().await {}
Ok(())
}
I will leave the low-level error for another answer, but I believe a more idiomatic way to solve the high-level problem here would be to combine the use of FuturesUnordered with something like tokio::select! as follows:
use tokio::sync::mpsc;
use futures::stream::FuturesUnordered;
use futures::StreamExt;
#[tokio::main]
pub async fn main() {
let mut futures = FuturesUnordered::new();
let (tx, mut rx) = mpsc::channel(32);
//turn foo into something more concrete
tokio::spawn(async move {
let _ = tx.send(42i32).await;
});
loop {
tokio::select! {
Some(data) = rx.recv() => {
futures.push(async move {
data.to_string()
});
},
Some(result) = futures.next() => {
println!("{}", result)
},
else => break,
}
}
}
You can read more about the select macro here: https://tokio.rs/tokio/tutorial/select
When you box the future created by the async block with the boxed method, you are trying to coerce it to a dyn Future + Send:
pub fn boxed<'a>(
self
) -> Pin<Box<dyn Future<Output = Self::Output> + 'a + Send>>
However, the created future is not Send. Why? Because inside of it, you try to push to the FuturesUnordered, which borrows it:
pub fn push(&self, future: Fut)
This means that the async block captures a &FuturesUnordered. For a type to be Send, all it's fields must be Send, so for the generated future to be Send, &FuturesUnordered must be Send.
For a reference to be Send, the type must also be Sync:
impl<'_, T> Send for &'_ T where
T: Sync
And for FuturesUnordered to be Sync, the stored futures must also be Sync:
impl<Fut: Sync> Sync for FuturesUnordered<Fut> {}
However, the future returned by boxed is not necessarily Sync:
pub fn boxed<'a>(
self
) -> Pin<Box<dyn Future<Output = Self::Output> + 'a + Send>>
Which means that the async generator is not Send, so you cannot coerce it to a dyn Future + Send, and you get a confusing error message.
The solution is to add a Sync bound to the future, and Box::pin manually:
type BoxedFuture = Pin<Box<dyn Future<Output = ()> + Send + Sync>>;
let mut futures = FuturesUnordered::<BoxedFuture>::new();
futures.push(Box::pin(async {
loop {
match rx.recv().await {
Some(data) => {
futures.push(Box::pin(async move {
let _ = data;
}));
}
None => {}
}
}
}));
However, you will then run into a bunch of borrowing issues. A better solution would be to use tokio::select! instead of the outer push, as explained by Michael's answer.
I am trying to write the contents of an HTTP Response to a file.
extern crate reqwest;
use std::io::Write;
use std::fs::File;
fn main() {
let mut resp = reqwest::get("https://www.rust-lang.org").unwrap();
assert!(resp.status().is_success());
// Write contents to disk.
let mut f = File::create("download_file").expect("Unable to create file");
f.write_all(resp.bytes());
}
But I get the following compile error:
error[E0308]: mismatched types
--> src/main.rs:12:17
|
12 | f.write_all(resp.bytes());
| ^^^^^^^^^^^^ expected &[u8], found struct `std::io::Bytes`
|
= note: expected type `&[u8]`
found type `std::io::Bytes<reqwest::Response>`
You cannot. Checking the docs for io::Bytes, there are no appropriate methods. That's because io::Bytes is an iterator that returns things byte-by-byte so there may not even be a single underlying slice of data.
It you only had io::Bytes, you would need to collect the iterator into a Vec:
let data: Result<Vec<_>, _> = resp.bytes().collect();
let data = data.expect("Unable to read data");
f.write_all(&data).expect("Unable to write data");
However, in most cases you have access to the type that implements Read, so you could instead use Read::read_to_end:
let mut data = Vec::new();
resp.read_to_end(&mut data).expect("Unable to read data");
f.write_all(&data).expect("Unable to write data");
In this specific case, you can use io::copy to directly copy from the Request to the file because Request implements io::Read and File implements io::Write:
extern crate reqwest;
use std::io;
use std::fs::File;
fn main() {
let mut resp = reqwest::get("https://www.rust-lang.org").unwrap();
assert!(resp.status().is_success());
// Write contents to disk.
let mut f = File::create("download_file").expect("Unable to create file");
io::copy(&mut resp, &mut f).expect("Unable to copy data");
}