The following code:
trait ClientResponse: DeserializeOwned + Send + Sized {}
struct ClientMsg {
...
resp: oneshot::Sender<Box<dyn ClientResponse>>
}
async fn client_thread(rx: mpsc::Receiver<ClientMsg>, client: reqwest::Client, base_url: Url) -> Result<(), Box<dyn Error>> {
while let Some(msg) = rx.recv().await {
...
let response = client.get(url).send().await?.json().await?;
msg.resp.send(response);
}
}
Fails with error:
error[E0038]: the trait `ClientResponse` cannot be made into an object
--> src/main.rs:16:11
|
16 | resp: oneshot::Sender<Box<dyn ClientResponse>>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ClientResponse` cannot be made into an object
|
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
--> src/main.rs:12:23
|
12 | trait ClientResponse: DeserializeOwned + Send + Sized {}
| -------------- ^^^^^^^^^^^^^^^^ ^^^^^ ...because it requires `Self: Sized`
| | |
| | ...because it requires `Self: Sized`
| this trait cannot be made into an object...
As you can see, I tried adding the Sized trait as a super trait after reading the compiler error, but it still gives me the same error. I'm not sure how else to approach this problem, since I want a client thread that can deserialize responses into types decided by the senders.
There is a crate exactly for making serde traits object safe called erased-serde
Related
Here is my code. In this program, I want to create a simple websocket server. When user sends a request to the ws://{url}/, the browser will establish a websocket connection with the server.
use std::{collections::HashMap, sync::Arc};
use async_std::{prelude::*, sync::Mutex};
use tide_websockets::WebSocket;
use uuid::Uuid;
#[async_std::main]
async fn main() {
let connections = Arc::new(Mutex::new(HashMap::new()));
let mut app = tide::new();
app.at("/").get(WebSocket::new(move |_, mut stream| async move {
let uuid = Uuid::new_v4();
// Add the connection to clients when opening a new connection
connections.lock().await.insert(uuid, stream.clone());
// Waiting for the connection to be closed
while let Some(Ok(_)) = stream.next().await {}
// Remove the connection from clients when it is closed
connections.lock().await.remove(&uuid);
Ok(())
}));
// app.bind(url).await
}
When I tried to compile this program, the rustc said:
error[E0507]: cannot move out of `connections`, a captured variable in an `Fn` closure
--> src/main.rs:11:57
|
9 | let connections = Arc::new(Mutex::new(HashMap::new()));
| ----------- captured outer variable
10 | let mut app = tide::new();
11 | app.at("/").get(WebSocket::new(move |_, mut stream| async move {
| ____________________________________--------------------_^
| | |
| | captured by this `Fn` closure
12 | | let uuid = Uuid::new_v4();
13 | |
14 | | // Add the connection to clients when opening a new connection
15 | | connections.lock().await.insert(uuid, stream.clone());
| | -----------
| | |
| | variable moved due to use in generator
| | move occurs because `connections` has type `Arc<async_std::sync::Mutex<HashMap<Uuid, WebSocketConnection>>>`, which does not implement the `Copy` trait
... |
23 | | Ok(())
24 | | }));
| |_____^ move out of `connections` occurs here
For more information about this error, try `rustc --explain E0507`.
error: could not compile `mre` due to previous error
And this is the definition of the Websocket::new method (no sure if it's useful):
impl<S, H, Fut> WebSocket<S, H>
where
S: Send + Sync + Clone + 'static,
H: Fn(Request<S>, WebSocketConnection) -> Fut + Sync + Send + 'static,
Fut: Future<Output = Result<()>> + Send + 'static,
{
/// Build a new WebSocket with a handler function that
pub fn new(handler: H) -> Self {
Self {
handler: Arc::new(handler),
ghostly_apparition: PhantomData,
protocols: Default::default(),
}
}
// ...
}
I tried searching this problem before posting this question. Most of the answers are either irrelevant, or need to modify the source code of the method (Websocket::new method here). But this method is not written by me but is from a third-party crate. Is there still any way to resolve this problem?
The argument of WebSocket::new() has to be an Fn closure, meaning, it must be callable repeatedly.
In your code, however, it internally uses the connections variable inside of an async move, meaning it moves the variable into the async block. This can for obvious reasons only be done once.
It's easy to fix, though. Instead of moving the entire connections variable in, you need to create a new Arc reference of the connections variable and move that one into the async move. So every invocation gets its own copy of it, making it compatible with Fn.
Here is a compiling version:
use std::{collections::HashMap, sync::Arc};
use async_std::{prelude::*, sync::Mutex};
use tide_websockets::WebSocket;
use uuid::Uuid;
#[async_std::main]
async fn main() {
let connections = Arc::new(Mutex::new(HashMap::new()));
let mut app = tide::new();
app.at("/").get(WebSocket::new(move |_, mut stream| {
let connections = Arc::clone(&connections);
async move {
let uuid = Uuid::new_v4();
// Add the connection to clients when opening a new connection
connections.lock().await.insert(uuid, stream.clone());
// Waiting for the connection to be closed
while let Some(Ok(_)) = stream.next().await {}
// Remove the connection from clients when it is closed
connections.lock().await.remove(&uuid);
Ok(())
}
}));
// app.bind(url).await
}
I have a warp server running, and for every request I need to compute a string and then return that string with a particular status code.
use warp::{http::StatusCode, reply, Filter};
let creator = warp::post()
.and(warp::path("mypath"))
.and(warp::body::bytes())
.and(warp::header("myheader"))
.map(move |buf: warp::hyper::body::Bytes, header: String| {
if (is_request_invalid()) {
reply::with_status("Request parameter xyz is invalid", StatusCode::BAD_REQUEST);
}
let computed_string: String = compute_from(buf, header);
return reply::with_status(
computed_string,
StatusCode::CREATED,
);
});
However, this doesn't work because reply::with_status requires a type of &str.
error[E0308]: mismatched types
--> lib.rs:54:33
|
54 | ... computed_string,
| ^^^^^^^^^^^^^^^
| |
| expected `&str`, found struct `std::string::String`
| help: consider borrowing here: `&computed_string`
So I tried:
// -- SNIP --
return reply::with_status(
&computed_string,
StatusCode::CREATED,
);
// -- SNIP --
(since &String derefs to &str) -- but this doesn't work either, as you can't return a reference to a variable owned by the current scope (will be dropped)
error[E0597]: `computed_string` does not live long enough
--> lib.rs:54:33
|
53 | return reply::with_status(
| ____________________________________-
54 | | &computed_string,
| | ^^^^^^^^^^^^^^^^ borrowed value does not live long enough
55 | | StatusCode::CREATED,
56 | | );
| |_____________________________- argument requires that `computed_string` is borrowed for `'static`
57 | }
| - `computed_string` dropped here while still borrowed
How do I return a String as a response when using warp?
The question didn't originally include the complete code, however it turns out there was a previous early-return which included an &str, which resulted in reply::with_status returning a WithStatus<&str>, which caused a mismatch when trying to return a WithStatus<String> in the same scope.
use warp::{http::StatusCode, reply, Filter};
let creator = warp::post()
.and(warp::path("mypath"))
.and(warp::body::bytes())
.and(warp::header("myheader"))
.map(move |buf: warp::hyper::body::Bytes, header: String| {
if (is_request_invalid()) {
return reply::with_status("Request parameter xyz is invalid", StatusCode::BAD_REQUEST);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type &str
}
let computed_string: String = compute_from(buf, header);
return reply::with_status(
computed_string,
// ^^^^^^^^^^^^^^^ type String
StatusCode::CREATED,
);
});
I'm on my way of converting to Rust from the ML family, but I'm finding it hard at some strange places I'm not used to having problems.
I'm trying to use hyper for http handling but can't seem to get tokio to work.
I have tried to copy paste this example:
use hyper::{body::HttpBody as _, Client};
use tokio::io::{self, AsyncWriteExt as _};
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
#[tokio::main]
async fn main() -> Result<()> {
// ...
fetch_url(url).await
}
async fn fetch_url(url: hyper::Uri) -> Result<()> {
// ...
Ok(())
}
Here is my Cargo.toml:
[package]
name = "projectname"
version = "0.1.0"
authors = ["username"]
edition = "2018"
[dependencies]
hyper = "0.14.4"
tokio = "1.2.0"
It is complaining that it can't find the io crate, and that main has an invalid type impl Future, and that it can't find main in tokio:
error[E0433]: failed to resolve: could not find `main` in `tokio`
--> src/main.rs:9:10
|
9 | #[tokio::main]
| ^^^^ could not find `main` in `tokio`
error[E0277]: `main` has invalid return type `impl Future`
--> src/main.rs:10:20
|
10 | async fn main() -> Result<()> {
| ^^^^^^^^^^ `main` can only return types that implement `Termination`
error[E0432]: unresolved import `hyper::Client`
--> src/main.rs:3:34
|
3 | use hyper::{body::HttpBody as _, Client};
| ^^^^^^ no `Client` in the root
error[E0425]: cannot find function `stdout` in module `io`
--> src/main.rs:45:13
|
45 | io::stdout().write_all(&chunk).await?;
| ^^^^^^ not found in `io`
|
error[E0432]: unresolved import `tokio::io::AsyncWriteExt`
--> src/main.rs:4:23
|
4 | use tokio::io::{self, AsyncWriteExt as _};
| -------------^^^^^
| |
| no `AsyncWriteExt` in `io`
| help: a similar name exists in the module: `AsyncWrite`
Is #[tokio::main] and client not in hyper?
The tokio::main macro converts an async main to a regular main that spawns a runtime. However, because the macro is not found is scope, it cannot transform your main function, and the compiler is complaining that your main has an invalid return type of impl Future. To fix this, you have to enable the required features to import the main macro:
tokio = { version = "1.2.0", features = ["rt", "macros"] }
You also have to enable the io-util feature to access io::AsyncWriteExt, and the io-std feature to access io::stdout. To simplify this, tokio provides the full feature flag, which will enable all optional features:
tokio = { version = "1.2.0", features = ["full"] }
You also need hyper's client and http feature flags to resolve the Client import:
hyper = { version = "0.14.4", features = ["client", "http1", "http2"] }
I'm trying to run/script my existing rust async code with rlua-async. Sadly it is not well documented and has no examples but I have prevailed in getting my async functions defined but I have trouble getting my lua code executed in an async way.
I have created a minimal repository to reproduce the problem here
use rlua::{Lua};
use rlua_async::{ChunkExt, ContextExt};
#[actix_rt::main]
async fn main() {
let lua_code = "my.asyncfunc(42)";
let lua = Lua::new();
lua.context(|lua_ctx| {
let globals = lua_ctx.globals();
let map_table = lua_ctx.create_table().unwrap();
map_table
.set(
"asyncfunc",
lua_ctx
.create_async_function(
|_ctx,
param:
u32
| async move {
println!("async function called {}", param);
Ok(())
}).unwrap()).unwrap();
globals.set("my", map_table).unwrap();
});
lua.context(|lua_context| async move {
let chunk = lua_context
.load(&lua_code);
chunk.exec_async(lua_context).await.unwrap();
})
.await;
println!("finished");
}
But I'm getting this error message:
error: lifetime may not live long enough
--> src\main.rs:28:31
|
28 | lua.context(|lua_context| async move {
| __________________------------_^
| | | |
| | | return type of closure is impl Future
| | has type `LuaContext<'1>`
29 | | let chunk = lua_context
30 | | .load(&lua_code);
31 | | chunk.exec_async(lua_context).await.unwrap();
32 | | })
| |_____^ returning this value requires that `'1` must outlive `'2`
I really don't get what the error is trying to tell me and there is no helpful tips or even documentation linked.
The closure is somehow different from the closure body and needs lifetime annotations? But why and how...?
EDIT: if I instead call the code without async like this:
lua.context(|lua_context| {
let chunk = lua_context.load(&lua_code);
chunk.exec().unwrap();
});
it compiles but I get the following panic on runtime:
thread 'main' panicked at 'cannot access a scoped thread local variable without calling `set` first', C:\Users\ahallmann\.cargo\registry\src\github.com-1ecc6299db9ec823\scoped-tls-1.0.0\src\lib.rs:168:9
Everything works fine if I define the function with create_function.
I figured it out with the kind help of the author of rlua-async.
The issue is with the actix-rt itself as it requires 'static lifetimes for the block_on call.
It works fine if you use either futures or tokio instead:
tokio::runtime::Runtime::new()
.unwrap()
.block_on(chunk.exec_async(ctx))
See https://github.com/actix/actix-net/issues/201
or
https://github.com/Ekleog/rlua-async/issues/1 for further information.
I am writing a set of mathematical functions using ndarray which I would like to perform on any type of ArrayBase. However, I'm having trouble specifying the traits/types involved.
This basic function works on either OwnedRepr or ViewRepr data:
use ndarray::{prelude::*, Data}; // 0.13.1
fn sum_owned(x: Array<f64, Ix1>) -> f64 {
x.sum()
}
fn sum_view(x: ArrayView<f64, Ix1>) -> f64 {
x.sum()
}
fn main() {
let a = Array::from_shape_vec((4,), vec![1.0, 2.0, 3.0, 4.0]).unwrap();
println!("{:?}", sum_owned(a.clone()));
let b = a.slice(s![..]);
println!("{:?}", sum_view(b));
// Complains that OwnedRepr is not ViewRepr
//println!("{:?}", sum_view(a.clone()));
}
I can understand why the commented out section doesn't compile, but I don't understand generics well enough to write something more... generic.
Here is what I tried:
use ndarray::prelude::*;
use ndarray::Data;
fn sum_general<S>(x: ArrayBase<S, Ix1>) -> f64
where
S: Data,
{
x.sum()
}
The compiler error suggests that Data is not specific enough, but I just can't parse it well enough to figure out what the solution should be:
error[E0277]: the trait bound `<S as ndarray::data_traits::RawData>::Elem: std::clone::Clone` is not satisfied
--> src/lib.rs:8:7
|
6 | S: Data,
| - help: consider further restricting the associated type: `, <S as ndarray::data_traits::RawData>::Elem: std::clone::Clone`
7 | {
8 | x.sum()
| ^^^ the trait `std::clone::Clone` is not implemented for `<S as ndarray::data_traits::RawData>::Elem`
error[E0277]: the trait bound `<S as ndarray::data_traits::RawData>::Elem: num_traits::identities::Zero` is not satisfied
--> src/lib.rs:8:7
|
6 | S: Data,
| - help: consider further restricting the associated type: `, <S as ndarray::data_traits::RawData>::Elem: num_traits::identities::Zero`
7 | {
8 | x.sum()
| ^^^ the trait `num_traits::identities::Zero` is not implemented for `<S as ndarray::data_traits::RawData>::Elem`
error[E0308]: mismatched types
--> src/lib.rs:8:5
|
4 | fn sum_general<S>(x: ArrayBase<S, Ix1>) -> f64
| --- expected `f64` because of return type
...
8 | x.sum()
| ^^^^^^^ expected `f64`, found associated type
|
= note: expected type `f64`
found associated type `<S as ndarray::data_traits::RawData>::Elem`
= note: consider constraining the associated type `<S as ndarray::data_traits::RawData>::Elem` to `f64`
= note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
If you look at the definition of the ndarray::ArrayBase::sum function that you're attempting to invoke:
impl<A, S, D> ArrayBase<S, D>
where
S: Data<Elem = A>,
D: Dimension,
{
pub fn sum(&self) -> A
where
A: Clone + Add<Output = A> + Zero
{
// etc.
}
}
It's clear that in your case A = f64 and D = Ix1, but you still need to specify the constraint S: Data<Elem = f64>. Therefore:
use ndarray::prelude::*;
use ndarray::Data;
fn sum_general<S>(x: ArrayBase<S, Ix1>) -> f64
where
S: Data<Elem = f64>,
{
x.sum()
}
Which is exactly what the compiler meant when it suggested:
= note: expected type `f64`
found associated type `<S as ndarray::data_traits::RawData>::Elem`
= note: consider constraining the associated type `<S as ndarray::data_traits::RawData>::Elem` to `f64`