Rust multiple crates are using different tokio versions - rust

I'm trying to build a application to monitor my local weather stations in Rust. I request a API using the reqwest crate and write data using the influxdb_rs crate. And it seems like that reqwest and influxdb_rs are using different tokio versions according to this error that varies depending on the version I set it to in the Cargo.toml file
there is no reactor running, must be called from the context of a Tokio 1.x runtime
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
//...
let api = netatmo::api::New(oauth).await?;
let station = api.clone().GetStation(DEVICE_ID_MAC.to_string()).await?;
let device: Device = station.body.devices[0].to_owned();
println!("Authenticated as {}", station.body.user.mail);
println!(
"Found {} Device Mac: [{}]",
station.body.devices.len(),
device.id
);
println!("Found {} Modules", device.modules.len());
for module in device.modules {
println!(
"Module Name: {:?} Mac: {} Type: {:?} Battery: {}",
module.module_name,
module.id,
module.data_type.join(","),
BatteryIndicator(module.battery_percent as i64)
)
}
// let client = influx::authenticate().await.unwrap();
// Tried to fix it by using the tokio-compat crate but did not work
let mut rt = Runtime::new()?;
let client = rt.block_on(async {
let authentication_handle = tokio::spawn(influx::authenticate());
let client = authentication_handle.await.unwrap();
client
});
let mut interval = interval(Duration::from_secs(300));
loop {
let station = api.clone().GetStation(DEVICE_ID_MAC.to_string()).await?;
let device: Device = station.body.devices[0].to_owned();
let mut device_dp = point!("indoor_device")
.add_field("wifi_status", Value::Integer(device.wifi_status))
.add_field("co2_level", Value::Integer(device.dashboard_data.co2))
.add_field("humidity", Value::Integer(device.dashboard_data.humidity))
.add_field("noise", Value::Integer(device.dashboard_data.noise))
.add_field("pressure", Value::Float(device.dashboard_data.pressure))
.add_field(
"temperature",
Value::Float(device.dashboard_data.temperature),
)
.add_timestamp(Utc::now().timestamp());
let write_err = client
.write_point(device_dp, Some(Precision::Minutes), None)
.await
.is_err();
if write_err {
println!("unable to write point");
}
My attempt to fix this resulted in this error:
error[E0277]: the trait bound `impl std::future::Future<Output = Result<influxdb_rs::Client, influxdb_rs::Error>>: futures::future::Future` is not satisfied
--> src\main.rs:54:30
|
54 | let client = rt.block_on(async {
| _____________________--------_^
| | |
| | required by a bound introduced by this call
55 | | let authentication_handle = tokio::spawn(influx::authenticate());
56 | | let client = authentication_handle.await.unwrap();
57 | | client
58 | | });
| |_____^ the trait `futures::future::Future` is not implemented for `impl std::future::Future<Output = Result<influxdb_rs::Client, influxdb_rs::Error>>`
|
= help: the following other types implement trait `futures::future::Future`:
&'a mut F
AssertUnwindSafe<F>
Box<F>
futures::future::and_then::AndThen<A, B, F>
futures::future::catch_unwind::CatchUnwind<F>
futures::future::either::Either<A, B>
futures::future::empty::Empty<T, E>
futures::future::flatten::Flatten<A>
and 58 others
note: required by a bound in `tokio_compat::runtime::Runtime::block_on`
--> C:\Users\matte\.cargo\registry\src\github.com-1ecc6299db9ec823\tokio-compat-0.1.6\src\runtime\threadpool\mod.rs:425:12
|
425 | F: Future01,
| ^^^^^^^^ required by this bound in `tokio_compat::runtime::Runtime::block_on`

Your source code references the netatmo crate, last updated in 2020. It depends on reqwests version 0.9, which, in turn, depends on tokio 0.1, which is incompatible with tokio 1.0. That is likely where the mismatch comes from.
You'll have to either fork and update the netamo crate yourself, or scrap it and implement it yourself.

Related

Share state between actix-web server and async closure

I want to periodically fetch data (using asynchronous reqwest), which is then served at an http endpoint using actix-web as a server.
(I have a data source that has a fixed format, that I want to have read by a service that require a different format, so I need to transform the data.)
I've tried to combine actix concepts with the thread sharing state example from the Rust book, but I don't understand the error or how to solve it.
This is the code minified as much as I was able:
use actix_web::{get, http, web, App, HttpResponse, HttpServer, Responder};
use std::sync::{Arc, Mutex};
use tokio::time::{sleep, Duration};
struct AppState {
status: String,
}
#[get("/")]
async fn index(data: web::Data<Mutex<AppState>>) -> impl Responder {
let state = data.lock().unwrap();
HttpResponse::Ok()
.insert_header(http::header::ContentType::plaintext())
.body(state.status.to_owned())
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let status_string = get_state().await.unwrap();
let app_data = Arc::new(Mutex::new(web::Data::new(AppState {
status: status_string,
})));
let app_data1 = Arc::clone(&app_data);
actix_web::rt::spawn(async move {
loop {
println!("I get executed every 2-ish seconds!");
sleep(Duration::from_millis(2000)).await;
let res = get_state().await;
let mut app_data = app_data1.lock().unwrap();
// Edit 2: this line is not accepted by the compiler
// Edit 2: *app_data.status = res.unwrap();
// Edit 2: but this line is accepted
*app_data = web::Data::new(AppState { status: res });
}
});
let app_data2 = Arc::clone(&app_data);
// Edit 2: but I get an error here now
HttpServer::new(move || App::new().app_data(app_data2).service(index))
.bind(("127.0.0.1", 9090))?
.run()
.await
}
async fn get_state() -> Result<String, Box<dyn std::error::Error>> {
let client = reqwest::Client::new().get("http://ipecho.net/plain".to_string());
let status = client.send().await?.text().await?;
println!("got status: {status}");
Ok(status)
}
But I get the following error:
error[E0308]: mismatched types
--> src/main.rs:33:32
|
33 | *app_data.status = res.unwrap();
| ---------------- ^^^^^^^^^^^^ expected `str`, found struct `String`
| |
| expected due to the type of this binding
error[E0277]: the size for values of type `str` cannot be known at compilation time
--> src/main.rs:33:13
|
33 | *app_data.status = res.unwrap();
| ^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `str`
= note: the left-hand-side of an assignment must have a statically known size
Some errors have detailed explanations: E0277, E0308.
For more information about an error, try `rustc --explain E0277`.
Why do I suddenly get a str? Is there an easy fix or is my approach to solving this wrong?
Edit: Maybe removing the * is the right way to go, as Peter Hall suggests, but that gives me the following error instead:
error[E0594]: cannot assign to data in an `Arc`
--> src/main.rs:33:13
|
33 | app_data.status = res.unwrap();
| ^^^^^^^^^^^^^^^ cannot assign
|
= help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Arc<AppState>`
error[E0507]: cannot move out of `app_data2`, a captured variable in an `Fn` closure
--> src/main.rs:38:49
|
37 | let app_data2 = Arc::clone(&app_data);
| --------- captured outer variable
38 | HttpServer::new(move || App::new().app_data(app_data2).service(index))
| ------- ^^^^^^^^^ move occurs because `app_data2` has type `Arc<std::sync::Mutex<Data<AppState>>>`, which does not implement the `Copy` trait
| |
| captured by this `Fn` closure
Some errors have detailed explanations: E0507, E0594.
For more information about an error, try `rustc --explain E0507`.
Edit 2: I now get the following error (code changes commented with 'Edit 2' above):
error[E0507]: cannot move out of `app_data2`, a captured variable in an `Fn` closure
--> src/main.rs:46:49
|
45 | let app_data2 = app_data.clone();
| --------- captured outer variable
46 | HttpServer::new(move || App::new().app_data(app_data2).service(index))
| ------- ^^^^^^^^^ move occurs because `app_data2` has type `Arc<Mutex<Data<AppState>>>`, which does not implement the `Copy` trait
| |
| captured by this `Fn` closure
For more information about this error, try `rustc --explain E0507`.
My Cargo.toml dependencies:
[dependencies]
actix-web = "4.2.1"
reqwest = "0.11.12"
tokio = "1.21.2"
async solution
I had my types mixed up a bit, having the app state as Arc<Mutex<T>> seemed to be the way to go, maybe it would be better with Arc<RwLock<T>>.
use actix_web::{get, http, web, App, HttpResponse, HttpServer, Responder};
use std::sync::{Arc, Mutex};
use tokio::time::{sleep, Duration};
struct AppState {
status: String,
}
#[get("/")]
async fn index(data: web::Data<Arc<Mutex<AppState>>>) -> impl Responder {
let state = data.lock().unwrap();
HttpResponse::Ok()
.insert_header(http::header::ContentType::plaintext())
.body(state.status.to_owned())
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let status_string = get_state().await.unwrap();
let app_data = Arc::new(Mutex::new(AppState {
status: status_string,
}));
let app_data1 = app_data.clone();
actix_web::rt::spawn(async move {
loop {
println!("I get executed every 2-ish seconds!");
sleep(Duration::from_millis(2000)).await;
let res = get_state().await.unwrap();
let mut app_data = app_data1.lock().unwrap();
*app_data = AppState { status: res };
}
});
HttpServer::new(move || {
App::new()
.app_data(web::Data::new(app_data.clone()))
.service(index)
})
.bind(("127.0.0.1", 9090))?
.run()
.await
}
async fn get_state() -> Result<String, Box<dyn std::error::Error>> {
let client = reqwest::Client::new().get("http://ipecho.net/plain".to_string());
let status = client.send().await?.text().await?;
println!("got status: {status}");
Ok(status)
}
async/sync solution
Instead of doing the async get with reqwest I have a solution with the synchronous crate minreq (that I found after a lot of searching). I also chose to not use the #[actix_web::main] macro, and instead start the runtime explicitly at the end of my main function.
use actix_web::{get, http, rt, web, App, HttpResponse, HttpServer, Responder};
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
struct AppState {
status: String,
}
#[get("/")]
async fn index(data: web::Data<Arc<Mutex<AppState>>>) -> impl Responder {
let state = &data.lock().unwrap();
HttpResponse::Ok()
.insert_header(http::header::ContentType::plaintext())
.body(state.status.clone())
}
fn main() -> std::io::Result<()> {
let status_string = get_state().unwrap();
let app_data = Arc::new(Mutex::new(AppState {
status: status_string,
}));
let app_data1 = Arc::clone(&app_data);
thread::spawn(move || loop {
thread::sleep(Duration::from_millis(2000));
let res = get_state().unwrap();
let mut app_data = app_data1.lock().unwrap();
*app_data = AppState { status: res };
});
rt::System::new().block_on(
HttpServer::new(move || {
App::new()
.app_data(web::Data::new(app_data.clone()))
.service(index)
})
.bind(("127.0.0.1", 9090))?
.run(),
)
}
fn get_state() -> Result<String, Box<dyn std::error::Error>> {
let resp = minreq::get("http://ipecho.net/plain").send().unwrap();
let state = resp.as_str().unwrap();
Ok(state.to_string())
}

"future cannot be sent between threads safely" when pass Arc<Mutex> into tokio::spawn

I implemented TCP client using tokio. However, my code not compile because I got an error:
error: future cannot be sent between threads safely
--> src/main.rs:81:9
|
81 | tokio::spawn(async move {
| ^^^^^^^^^^^^ future created by async block is not `Send`
|
= help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `std::sync::MutexGuard<'_, Option<tokio::net::TcpStream>>`
note: future is not `Send` as this value is used across an await
--> src/main.rs:90:42
|
82 | match stream.lock().unwrap().as_mut() {
| ---------------------- has type `std::sync::MutexGuard<'_, Option<tokio::net::TcpStream>>` which is not `Send`
...
90 | stream.write(&packet).await.unwrap();
| ^^^^^^ await occurs here, with `stream.lock().unwrap()` maybe used later
...
94 | };
| - `stream.lock().unwrap()` is later dropped here
help: consider moving this into a `let` binding to create a shorter lived borrow
--> src/main.rs:82:19
|
82 | match stream.lock().unwrap().as_mut() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: required by a bound in `tokio::spawn`
--> /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.19.2/src/task/spawn.rs:127:21
|
127 | T: Future + Send + 'static,
| ^^^^ required by this bound in `tokio::spawn`
This is my code where issue occurs:
async fn handle_write(&mut self) -> JoinHandle<()> {
let stream = Arc::clone(&self.stream);
let session = Arc::clone(&self.session);
let queue = Arc::clone(&self.queue);
tokio::spawn(async move {
match stream.lock().unwrap().as_mut() {
Some(stream) => {
let packet: Vec<u8> = queue.lock().unwrap().pop_front().unwrap();
let packet = match session.lock().unwrap().header_crypt.as_mut() {
Some(header_crypt) => header_crypt.encrypt(&packet),
_ => packet,
};
stream.write(&packet).await.unwrap();
stream.flush().await.unwrap();
},
_ => {},
};
})
}
and same issue here:
async fn handle_read(&mut self) -> JoinHandle<()> {
let queue = Arc::clone(&self.queue);
let stream = Arc::clone(&self.stream);
let session = Arc::clone(&self.session);
tokio::spawn(async move {
match stream.lock().unwrap().as_mut() {
Some(stream) => {
let mut buffer = [0u8; 4096];
match stream.read(&mut buffer).await {
Ok(bytes_count) => {
let raw_data = match session.lock().unwrap().header_crypt.as_mut() {
Some(header_crypt) => header_crypt.decrypt(&buffer[..bytes_count]),
_ => buffer[..bytes_count].to_vec(),
};
queue.lock().unwrap().push_back(raw_data);
},
_ => {},
};
},
_ => {},
};
})
}
Playground.
Could somebody explain, what am I doing wrong ?
P.S. just in case: I am using std::sync::{Arc, Mutex};
Finally, as decided in comments to my question, I used tokio::sync::Mutex instead of std::sync::Mutex. So, now code compiles correctly.
Playground.
In my case the problem was using ThreadRng with thread_rng() which is NOT thread safe. Just as a heads-up for anyone else banging their head against this error message. I refactored to using let mut rng = ::rand::rngs::StdRng::from_seed(OsRng.gen());

How to share tokio::net::TcpStream act on it concurrently?

I have a requirement to send and receive normal data on the same TcpStream, while sending heartbeat data at regular intervals. In the current implementation, I used Arc<Mutex<TcpStream>>, but it compiled with errors:
use anyhow::Result;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpStream;
#[tokio::main]
async fn main() -> Result<()> {
let stream = TcpStream::connect("127.0.0.1:8888").await.unwrap();
let stream = Arc::new(Mutex::new(stream));
let common_stream = stream.clone();
let handler1 = tokio::spawn(async {
loop {
let mut stream = common_stream.lock().unwrap();
let mut buf = [0u8; 10];
stream.read_exact(&mut buf).await.unwrap();
buf.reverse();
stream.write(&buf).await.unwrap();
}
});
let heartbeat_stream = stream.clone();
let handler2 = tokio::spawn(async {
loop {
let mut stream = heartbeat_stream.lock().unwrap();
stream.write_u8(1).await.unwrap();
thread::sleep(Duration::from_millis(200));
}
});
handler1.await?;
handler2.await?;
Ok(())
}
error: future cannot be sent between threads safely
--> src\main.rs:14:20
|
14 | let handler1 = tokio::spawn(async {
| ^^^^^^^^^^^^ future created by async block is not `Send`
|
= help: within `impl Future<Output = [async output]>`, the trait `Send` is not implemented for `std::sync::MutexGuard<'_, tokio::net::TcpStream>`
note: future is not `Send` as this value is used across an await
--> src\main.rs:20:31
|
16 | let mut stream = common_stream.lock().unwrap();
| ---------- has type `std::sync::MutexGuard<'_, tokio::net::TcpStream>` which is not `Send`
...
20 | stream.write(&buf).await.unwrap();
| ^^^^^^ await occurs here, with `mut stream` maybe used later
21 | }
| - `mut stream` is later dropped here
note: required by a bound in `tokio::spawn`
--> .cargo\registry\src\mirrors.tuna.tsinghua.edu.cn-df7c3c540f42cdbd\tokio-1.17.0\src\task\spawn.rs:127:21
|
127 | T: Future + Send + 'static,
| ^^^^ required by this bound in `tokio::spawn`
error: future cannot be sent between threads safely
--> src\main.rs:25:20
|
25 | let handler2 = tokio::spawn(async {
| ^^^^^^^^^^^^ future created by async block is not `Send`
|
= help: within `impl Future<Output = [async output]>`, the trait `Send` is not implemented for `std::sync::MutexGuard<'_, tokio::net::TcpStream>`
note: future is not `Send` as this value is used across an await
--> src\main.rs:28:31
|
27 | let mut stream = heartbeat_stream.lock().unwrap();
| ---------- has type `std::sync::MutexGuard<'_, tokio::net::TcpStream>` which is not `Send`
28 | stream.write_u8(1).await.unwrap();
| ^^^^^^ await occurs here, with `mut stream` maybe used later
...
31 | }
| - `mut stream` is later dropped here
note: required by a bound in `tokio::spawn`
--> .cargo\registry\src\mirrors.tuna.tsinghua.edu.cn-df7c3c540f42cdbd\tokio-1.17.0\src\task\spawn.rs:127:21
|
127 | T: Future + Send + 'static,
| ^^^^ required by this bound in `tokio::spawn`
How can these errors be fixed or is there another way to achieve the same goal?
Here is a solution that splits the stream to two parts for reading and writing plus does in a loop:
waiting for heartbeat events and sends a byte to write half of stream when this happens
waits data from read half (10 bytes), reverses it and writes again to write half
Also this does not spawn threads and does everything nicely in current one without locks.
use anyhow::Result;
use std::time::Duration;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpStream;
#[tokio::main]
async fn main() -> Result<()> {
let mut stream = TcpStream::connect("127.0.0.1:8888").await?;
let (mut read, mut write) = stream.split();
let mut heartbeat_interval = tokio::time::interval(Duration::from_millis(200));
let mut buf = [0u8; 10];
loop {
tokio::select! {
_ = heartbeat_interval.tick() => {
write.write(&[1]).await?;
}
result = read.read_exact(&mut buf) => {
let _bytes_read = result?;
buf.reverse();
write.write(&buf).await?;
}
}
}
}
Several errors in your code, although the idea behind it is almost good. You should use any available tool in async as possible. Some of the needed/desired changes:
Use tokio::time::sleep because it is async, otherwise the call is blocking
Use an async version of mutex (the one from futures crate for example)
Use some kind of generic error handling (anyhow would help)
use futures::lock::Mutex;
use anyhow::Error;
use tokio::time::sleep;
use std::sync::Arc;
use std::time::Duration;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpStream;
#[tokio::main]
async fn main() -> Result<(), Error> {
let stream = TcpStream::connect("127.0.0.1:8888").await.unwrap();
let stream = Arc::new(Mutex::new(stream));
let common_stream = stream.clone();
let handler1 = tokio::spawn(async move {
loop {
let mut stream = common_stream.lock().await;
let mut buf = [0u8; 10];
stream.read_exact(&mut buf).await.unwrap();
buf.reverse();
stream.write(&buf).await.unwrap();
}
});
let heartbeat_stream = stream.clone();
let handler2 = tokio::spawn(async move {
loop {
let mut stream = heartbeat_stream.lock().await;
stream.write_u8(1).await.unwrap();
sleep(Duration::from_millis(200)).await;
}
});
handler1.await?;
handler2.await?;
Ok(())
}
Playground

Why does one of these two very similar async Rust functions trigger thread safety errors?

I'm trying something as a learn-Rust project, where I have a library that consumes some REST APIs through a HTTP request trait that I planned to fill in separately for native and webassembly usage, so that I could have bindings for this library in different environments.
My problem arises in the WASM portion, where I'm trying to adapt the fetch example here:
pub async fn run(url: String, authz: String) -> Result<serde_json::Value, JsValue> {
let mut opts = RequestInit::new();
opts.method("GET");
opts.mode(RequestMode::Cors);
let request = Request::new_with_str_and_init(&url, &opts)?;
request.headers().set("Authorization", &authz)?;
let window = web_sys::window().unwrap();
let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?;
assert!(resp_value.is_instance_of::<Response>());
let resp: Response = resp_value.dyn_into().unwrap();
let json = JsFuture::from(resp.json()?).await?;
let parsed: serde_json::Value = json.into_serde().unwrap();
Ok(parsed)
}
I made some light adaptations to suit my purpose here, but it works fine. What doesn't work is my attempt to shoehorn this into the trait interface. The return type is an anyhow::Result, and I unwrap() a bunch of stuff I shouldn't here to temporarily avoid issues with the error type compatibility:
#[async_trait]
impl FetchRequest for WasmFetchRequest {
async fn get(&mut self) -> Result<serde_json::Value> {
let mut opts = RequestInit::new();
opts.method("GET");
opts.mode(RequestMode::Cors);
let request = Request::new_with_str_and_init(&self.path, &opts).unwrap();
request.headers().set("Authorization", &self.authz);
let window = web_sys::window().unwrap();
let resp_value = JsFuture::from(window.fetch_with_request(&request)).await.unwrap();
assert!(resp_value.is_instance_of::<Response>());
let resp: Response = resp_value.dyn_into().unwrap();
let json = JsFuture::from(resp.json().unwrap()).await.unwrap();
let parsed: serde_json::Value = resp.into_serde().unwrap();
Ok(parsed)
/*
// hoped this might work too, same errors
Ok(run(
self.path.to_string(),
self.authz.to_string()
).await.map_err(|err| err.into())?);
*/
}
}
The build is unhappy:
error: future cannot be sent between threads safely
--> src/lib.rs:66:58
|
66 | async fn get(&mut self) -> Result<serde_json::Value> {
| __________________________________________________________^
67 | | /*
68 | | Ok(run(
69 | | self.path.to_string(),
... |
91 | | Ok(parsed)
92 | | }
| |_____^ future created by async block is not `Send`
|
= help: within `impl Future`, the trait `Send` is not implemented for `Rc<RefCell<wasm_bindgen_futures::Inner>>`
note: future is not `Send` as it awaits another future which is not `Send`
--> src/lib.rs:83:26
|
83 | let resp_value = JsFuture::from(window.fetch_with_request(&request)).await.unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ await occurs here on type `JsFuture`, which is not `Send`
= note: required for the cast to the object type `dyn Future<Output = Result<Value, rescale_core::Error>> + Send`
error: future cannot be sent between threads safely
--> src/lib.rs:66:58
|
66 | async fn get(&mut self) -> Result<serde_json::Value> {
| __________________________________________________________^
67 | | /*
68 | | Ok(run(
69 | | self.path.to_string(),
... |
91 | | Ok(parsed)
92 | | }
| |_____^ future created by async block is not `Send`
|
= help: within `impl Future`, the trait `Send` is not implemented for `*mut u8`
note: future is not `Send` as this value is used across an await
--> src/lib.rs:88:20
|
83 | let resp_value = JsFuture::from(window.fetch_with_request(&request)).await.unwrap();
| ---------- has type `wasm_bindgen::JsValue` which is not `Send`
...
88 | let json = JsFuture::from(resp.json().unwrap()).await.unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ await occurs here, with `resp_value` maybe used later
...
92 | }
| - `resp_value` is later dropped here
= note: required for the cast to the object type `dyn Future<Output = Result<Value, rescale_core::Error>> + Send`
error: future cannot be sent between threads safely
--> src/lib.rs:66:58
|
66 | async fn get(&mut self) -> Result<serde_json::Value> {
| __________________________________________________________^
67 | | /*
68 | | Ok(run(
69 | | self.path.to_string(),
... |
91 | | Ok(parsed)
92 | | }
| |_____^ future created by async block is not `Send`
|
= help: within `Request`, the trait `Sync` is not implemented for `*mut u8`
note: future is not `Send` as this value is used across an await
--> src/lib.rs:83:26
|
83 | let resp_value = JsFuture::from(window.fetch_with_request(&request)).await.unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ first, await occurs here, with `&request` maybe used later...
note: `&request` is later dropped here
--> src/lib.rs:83:92
|
83 | let resp_value = JsFuture::from(window.fetch_with_request(&request)).await.unwrap();
| -------- ^
| |
| has type `&Request` which is not `Send`
help: consider moving this into a `let` binding to create a shorter lived borrow
--> src/lib.rs:83:41
|
83 | let resp_value = JsFuture::from(window.fetch_with_request(&request)).await.unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: required for the cast to the object type `dyn Future<Output = Result<Value, rescale_core::Error>> + Send`
What is the pertinent difference here that causes these errors? I think it must be something with &mut self or else the return type, but I'm out of my depth.
Probably not relevant, but the native counterpart fits into the interface happily enough:
#[async_trait]
impl FetchRequest for NativeFetchRequest {
async fn get(&mut self) -> Result<serde_json::Value> {
let client = reqwest::Client::new();
let mut req = client.get(&self.path);
req = req.header("Authorization", self.authz.as_str());
let res = req.send().await?;
res.error_for_status()?
.json::<serde_json::Value>().await
.map_err(|err| err.into())
}
}
According to async_trait documentation, futures returned by async trait methods must by default be Send:
Async fns get transformed into methods that return Pin<Box<dyn Future + Send + 'async>> and delegate to a private async freestanding function.
Your async fn produced a non-Send future. So the difference between your original code and the one that uses async_trait was that the original code didn't require a Send future, it was okay with non-Send ones, whereas async_trait by default expects Send futures.
To fix the issue, you need to tell async_trait not to require Send using #[async_trait(?Send)] on the trait and the impl block. In other words, replace #[async_trait] with #[async_trait(?Send)] in both the trait declaration and the implementation, and your code should compile. (Playground.)

What missing functionality and traits do I need Spawn_ok with a clonable object?

I'm experimenting with futures 0.3 for the first time, and getting trait or lifetime issues.
use futures::executor::ThreadPool;
use futures::future::*;
use futures::StreamExt;
const BUFSIZE: usize = 140;
#[derive(Clone)]
struct Packet {
channel: usize,
seq: usize,
total: usize,
buffer: [u8; BUFSIZE],
}
fn to_packets<T>(channel: usize, msg: T) -> Vec<Packet> {
// Trivial implemmentation
vec![Packet {
channel: 0,
seq: 0,
total: 0,
buffer: [0u8; BUFSIZE],
}]
}
pub struct SMConnection;
impl SMConnection {
fn push<T: 'static>(&mut self, channel: &'static usize, msg: T)
where
T: Send + Sync + Clone + Serialize,
{
let threadpool = ThreadPool::new().unwrap();
let future = async {
move || {
to_packets(*channel, msg)
.iter()
.for_each(|_packet| { /* do something */ })
};
};
threadpool.spawn_ok(future);
}
}
This errors with
error[E0597]: `channel` does not live long enough
--> src\network.rs:82:27
|
81 | let future = async {
| ______________________-_____-
| |______________________|
| ||
82 | || let channel = channel.clone();
| || ^^^^^^^ borrowed value does not live long enough
83 | || move || to_packets(channel, msg).iter().for_each(|_| { });
84 | || };
| || -
| ||_________|
| |__________value captured here by generator
| argument requires that `channel` is borrowed for `'static`
85 | threadpool.spawn_ok(future);
86 | }
| - `channel` dropped here while still borrowed
I'm passing channel and msg by value so I would not expect there to be a lifetime issue. Following the compiler advice to give 'static bounds to the arguments still leaves me stuck.
I've tried combinations of clone and Arc
What have I missed here?
EDIT For reference: Cargo.toml
[dependencies]
serde="^1"
serde_derive="^1"
bincode = "1.1.4"
romio = "0.3.0-alpha.9"
futures-preview = { version = "=0.3.0-alpha.18", features = ["async-await", "nightly"] }
Compiler: rustc 1.39.0-nightly (9b91b9c10 2019-08-26)
After playing around with it and specifically reading How can I send non-static data to a thread in Rust and is it needed in this example?, I realised that you can keep the interface the same and shield it from users if you 1) move the value into the async future and 2) wrap the values in an Arc and using the values from behind the reference counted wrapper
fn push<T: 'static>(&mut self, channel: usize, msg: T)
where
T: Send + Sync + Clone + Serialize,
{
let threadpool = ThreadPool::new().unwrap();
let future = async move {
let msg = Arc::from(msg);
let channel = Arc::from(channel);
move || to_packets(*channel, msg).iter().for_each(|_| {});
};
threadpool.spawn_ok(future);
}

Resources