Multiple actix-web client requests - expected struct actix_web::Error found () - rust

I have this code:
extern crate actix;
extern crate actix_web;
extern crate env_logger;
extern crate futures; // 0.1.25
extern crate tokio; // 0.1.17
use futures::future::ok as fut_ok;
use futures::Future;
use tokio::runtime::Builder;
use std::sync::{Arc, Mutex};
extern crate serde_json;
type Error = ();
fn questions_data(
val: i32,
) -> Box<Future<Item = serde_json::Value, Error = actix_web::error::Error>> {
let f = std::fs::read_to_string("auth_token").unwrap();
let token = f.trim();
use actix_web::{client, HttpMessage};
use std::time::Duration;
Box::new(
client::ClientRequest::get(
"https://jsonplaceholder.typicode.com/todos/".to_owned() + &val.to_string(),
)
.header(
actix_web::http::header::AUTHORIZATION,
"Bearer ".to_owned() + token,
)
.finish()
.unwrap()
.send()
.timeout(Duration::from_secs(30))
.map_err(actix_web::error::Error::from) // <- convert SendRequestError to an Error
.and_then(|resp| {
resp.body().limit(67_108_864).from_err().and_then(|body| {
let resp: serde_json::Value = serde_json::from_slice(&body).unwrap();
fut_ok(resp)
})
}),
)
}
fn main() {
let num_workers = 8;
let mut core = Builder::new().core_threads(num_workers).build().unwrap();
let results = Arc::new(Mutex::new(Vec::new()));
for n in 1..100 {
let res = results.clone();
core.spawn(questions_data(n).map(move |n| {
res.lock().unwrap().push(n);
}));
}
core.shutdown_on_idle().wait().unwrap();
let data = results.lock().unwrap();
println!("{:?}", *data);
}
[dependencies]
futures = "0.1.25"
tokio-core = "0.1.17"
futures-util = "0.2.1"
tokio = "0.1.11"
rand = "0.6.0"
actix-web = "0.7.14"
actix = "0.7.6"
env_logger = "0.6.0"
serde_json = "1.0.33"
I get error when I cargo run it:
error[E0271]: type mismatch resolving `<std::boxed::Box<futures::Map<std::boxed::Box<dyn futures::Future<Item=serde_json::Value, Error=actix_web::Error>>, [closure#src/main.rs:52:51: 54:10 res:_]>> as futures::Future>::Error == ()`
--> src/main.rs:52:14
|
52 | core.spawn(Box::new(questions_data(n).map(move |n| {
| ^^^^^ expected struct `actix_web::Error`, found ()
|
= note: expected type `actix_web::Error`
found type `()`
error[E0277]: `dyn futures::Future<Item=serde_json::Value, Error=actix_web::Error>` cannot be sent between threads safely
--> src/main.rs:52:14
|
52 | core.spawn(Box::new(questions_data(n).map(move |n| {
| ^^^^^ `dyn futures::Future<Item=serde_json::Value, Error=actix_web::Error>` cannot be sent between threads safely
|
= help: the trait `std::marker::Send` is not implemented for `dyn futures::Future<Item=serde_json::Value, Error=actix_web::Error>`
= note: required because of the requirements on the impl of `std::marker::Send` for `std::ptr::Unique<dyn futures::Future<Item=serde_json::Value, Error=actix_web::Error>>`
= note: required because it appears within the type `std::boxed::Box<dyn futures::Future<Item=serde_json::Value, Error=actix_web::Error>>`
= note: required because it appears within the type `futures::Map<std::boxed::Box<dyn futures::Future<Item=serde_json::Value, Error=actix_web::Error>>, [closure#src/main.rs:52:51: 54:10 res:_]>`
= note: required because of the requirements on the impl of `std::marker::Send` for `std::ptr::Unique<futures::Map<std::boxed::Box<dyn futures::Future<Item=serde_json::Value, Error=actix_web::Error>>, [closure#src/main.rs:52:51: 54:10 res:_]>>`
= note: required because it appears within the type `std::boxed::Box<futures::Map<std::boxed::Box<dyn futures::Future<Item=serde_json::Value, Error=actix_web::Error>>, [closure#src/main.rs:52:51: 54:10 res:_]>>`
Similar code, without running actix-web client, works https://play.rust-lang.org/?version=stable&mode=debug&edition=2015&gist=e81185a73fcb40a3426875573c78accd
EDIT:
Maybe something like map_err(|_| ()) but don’t know how to apply that:
https://users.rust-lang.org/t/type-error-when-combining-streams/21619
Type mismatch resolving the error type when forwarding messages from a futures channel to a WebSocket Sink

As rustc says there are two errors here:
First is about error types mismatch. It could be fixed with map_err as you already noticed:
questions_data(n)
.map(move |n| {
res.lock().unwrap().push(n);
})
.map_err(|e| println!("an error occurred: {}", e))
Second (about Send marker) is somewhat more confusing. Basically it means that tokio::Runtime::spawn want its argument future to be also Send + 'static because tokio could move it to another thread. But your future cannot be send between threads safely because it contains non-sendable type (dyn futures::Stream<Item=bytes::bytes::Bytes, Error=actix_web::Error> + 'static) inside actix_web::client::ClientRequest type.
Probably the easiest way to fix the second error is to use actix::spawn function instead tokio::Runtime::spawn method: it requires only 'static marker for its argument.
After these changes your program compiles at least.

Related

no method named `json` found for opaque type `impl std::future::Future`

I want to use reqwest to write a rest client, follow the official document https://github.com/seanmonstar/reqwest and add dependencies like this in toml:
reqwest = {version = "0.11.4", feature = ["blocking", "json"]}
and this is my code:
use std::collections::HashMap;
pub fn get_user_info(id: i64) -> &'static str {
let resp = reqwest::get("https://httpbin.org/ip")
.json::<HashMap<String, String>>();
println!("{:#?}", resp);
return "ok";
}
when I compile this code, shows error like this:
error[E0599]: no method named `json` found for opaque type `impl std::future::Future` in the current scope
--> src/common/net/rest_client.rs:5:10
|
5 | .json::<HashMap<String, String>>();
| ^^^^ method not found in `impl std::future::Future`
why would this happen? what should I do to fix this problem?
You are using asynchronous version of the GET API. If you want to use the blocking one, Use reqwest::blocking::get instead.
Since you enabled the blocking feature in Cargo.toml, I am assuming that you intended to use the blocking API.
use std::collections::HashMap;
pub fn get_info() -> &'static str {
let resp = reqwest::blocking::get("https://httpbin.org/ip")
.unwrap()
.json::<HashMap<String, String>>()
.unwrap();
println!("{:#?}", resp);
return "ok";
}
If you still want to use async API, you have to await the result before deserializing the response body.

How can I create a generic type that includes a trait

I find I am using this pattern a lot.
Arc<Mutex<dyn SomeTrait + Send>>;
and so I thought I would do this:
pub type NicePtr<T> = Arc<Mutex<dyn T + Send>>;
but this does not compile
Compiling rsim v0.1.0 (C:\work\pdp\rsim)
error[E0404]: expected trait, found type parameter `T`
--> src\common.rs:9:37
|
9 | pub type NicePtr<T> = Arc<Mutex<dyn T + Send>>;
| ^ not a trait
I assume this is possible, but I just dont know the correct syntax.
An interesting note is that (it is the thing that you want)
use std::sync::{Arc, Mutex};
pub type NicePtr<T: Send> = Arc<Mutex<T>>;
does compile, but with a warning:
warning: bounds on generic parameters are not enforced in type aliases
--> src/lib.rs:2:21
|
2 | pub type NicePtr<T: Send> = Arc<Mutex<T>>;
| ^^^^
|
= note: `#[warn(type_alias_bounds)]` on by default
help: the bound will not be checked when the type alias is used, and should be removed
|
2 - pub type NicePtr<T: Send> = Arc<Mutex<T>>;
2 + pub type NicePtr<T> = Arc<Mutex<T>>;
|
As a comment points out, you can accomplish something similar to what you want with macros. You could have one macro that expands to a type and one that wraps Arc::new(Mutex::new()) (or any other pointer type that you decide is nice).
use std::sync::{Arc, Mutex};
use std::fmt::Display;
macro_rules! nice_ptr {
// for types
($t:ty) => {
Arc<Mutex<$t>>
};
// for traits
($t:expr) => {
Arc<Mutex<$t>>
}
}
macro_rules! nice_ptr_new{
($inner:expr) => {
Arc::new(Mutex::new($inner))
}
}
fn main() {
let example: nice_ptr!(dyn Display) = nice_ptr_new!("example");
println!("{}", example.lock().unwrap());
}
Using it with the wrong types even gives a helpful error message.
fn main() {
// arrays don't implement Display!
let example: nice_ptr!(dyn Display) = nice_ptr_new!([1,2,3]);
println!("{}", example.lock().unwrap());
}
15 | Arc::new(Mutex::new($inner))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `[{integer}; 3]` cannot be formatted with the default formatter
...
20 | let example: nice_ptr!(dyn Display) = nice_ptr_new!([1,2,3]);
| ---------------------- in this macro invocation
|
= help: the trait `std::fmt::Display` is not implemented for `[{integer}; 3]`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
= note: required for the cast to the object type `dyn std::fmt::Display`
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

Handlebars helpers within Rocket

I am trying to register a helper for handlebars the ultimate goal is to be able to have a helper that calculates/prints the length of a Vec. But I already fail to get the samples from the documentation into my program. I managed to do a 'minimal' example that shows the same error as my rocket page.
#[macro_use]
extern crate handlebars;
extern crate rocket;
extern crate rocket_contrib;
use handlebars::{Context, Handlebars, Helper, HelperResult, JsonRender, Output, RenderContext};
use rocket_contrib::templates::Template;
handlebars_helper!(hex: |v: i64| format!("0x{:x}", v));
fn wow_helper(
h: &Helper,
_: &Handlebars,
_: &Context,
_: &mut RenderContext,
out: &mut Output,
) -> HelperResult {
if let Some(param) = h.param(0) {
out.write("<b><i>")?;
out.write(&param.value().render())?;
out.write("</b></i>")?;
}
Ok(())
}
fn main() {
rocket::ignite()
.attach(Template::custom(|engines| {
engines
.handlebars
.register_helper("mlenb", Box::new(wow_helper));
}))
.launch();
println!("Hello, world!");
}
with the dependencies in cargo.toml:
[dependencies]
handlebars="3.3.0"
rocket="0.4.5"
[dependencies.rocket_contrib]
version = "0.4.5"
default-features = false
features = ["diesel_sqlite_pool", "handlebars_templates", "serve"]
The error:
error[E0631]: type mismatch in function arguments
--> src/main.rs:184:43
|
162 | / fn wow_helper(
163 | | h: &handlebars::Helper,
164 | | _: &handlebars::Handlebars,
165 | | _: &handlebars::Context,
... |
175 | | Ok(())
176 | | }
| |_- found signature of `for<'r, 's, 't0, 't1, 't2, 't3, 't4, 't5, 't6, 't7> fn(&'r handlebars::Helper<'s, 't0>, &'t1 handlebars::Handlebars<'t2>, &'t3 handlebars::Context, &'t4 mut handlebars::RenderContext<'t5, 't6>, &'t7 mut (dyn handlebars::Output + 't7)) -> _`
...
184 | .register_helper("mlenb", Box::new(wow_helper));
| ^^^^^^^^^^^^^^^^^^^^ expected signature of `for<'r, 'reg, 'rc, 's, 't0> fn(&'r rocket_contrib::templates::handlebars::Helper<'reg, 'rc>, &'reg rocket_contrib::templates::handlebars::Handlebars, &'rc rocket_contrib::templates::handlebars::Context, &'s mut rocket_contrib::templates::handlebars::RenderContext<'reg>, &'t0 mut (dyn rocket_contrib::templates::handlebars::Output + 't0)) -> _`
|
= note: required because of the requirements on the impl of `rocket_contrib::templates::handlebars::HelperDef` for `for<'r, 's, 't0, 't1, 't2, 't3, 't4, 't5, 't6, 't7> fn(&'r handlebars::Helper<'s, 't0>, &'t1 handlebars::Handlebars<'t2>, &'t3 handlebars::Context, &'t4 mut handlebars::RenderContext<'t5, 't6>, &'t7 mut (dyn handlebars::Output + 't7)) -> std::result::Result<(), handlebars::RenderError> {wow_helper}`
= note: required for the cast to the object type `dyn rocket_contrib::templates::handlebars::HelperDef`
Don't need to use crate handlebars in cargo.toml with rocket crate.
its works well like that:
in cargo.toml only
[dependencies]
rocket = "0.4.5"
[dependencies.rocket_contrib]
version = "0.4.5"
default-features = false
features = ["handlebars_templates", "serves"]
and in the main.rs use:
use rocket_contrib :: templates :: {Template, handlebars};
use handlebars :: {Helper, Handlebars, Context, RenderContext, Output, HelperResult, RenderError};
So there will be no problems with versioning the handlebars!
Thanks to #jebrosen:mozilla.org in the rocket matrix channel the problem got resolved.
Problem:
I used incompatible versions of handlebars. The rocket library uses version 1.0 while I used Version 3.0 in the Cargo.toml.
Solution:
Change the Cargo.toml to:
[dependencies]
handlebars="1.0"
rocket="0.4.5"
[dependencies.rocket_contrib]
version = "0.4.5"
default-features = false
features = ["diesel_sqlite_pool", "handlebars_templates", "serve"]
Careful a rebuild requires a cargo clean otherwise the changes don't get built.
Solution for the initial Problem:
I did not manage to use the macro handlebars_helper! to create the length helper but instead created it using the function syntax.
fn mlen_helper_fun(
h: &handlebars::Helper<'_, '_>,
_: &handlebars::Handlebars,
_: &handlebars::Context,
_: &mut handlebars::RenderContext<'_>,
out: &mut dyn handlebars::Output,
) -> handlebars::HelperResult {
if let Some(param) = h.param(0) {
if let serde_json::value::Value::Array(arr) = param.value() {
out.write(&format!("{}", arr.len()))?;
Ok(())
} else {
Err(handlebars::RenderError::new("Not a array"))
}
} else {
Err(handlebars::RenderError::new(
"No parameter given at least one required",
))
}
}
This can then be used with:
.attach(Template::custom(|engines| {
engines
.handlebars
.register_helper("length", Box::new(mlen_helper_fun));
}))

The example from the "chaining computations" section of the Tokio docs does not compile: "expected struct `std::io::Error`, found ()"

I am learning Tokio. I read Getting asynchronous from the official docs, but the source code from the Chaining computations section cannot be compiled under the latest Rust version (Rust 2018, v1.31):
extern crate tokio;
extern crate bytes;
#[macro_use]
extern crate futures;
use tokio::io::AsyncWrite;
use tokio::net::{TcpStream, tcp::ConnectFuture};
use bytes::{Bytes, Buf};
use futures::{Future, Async, Poll};
use std::io::{self, Cursor};
// HelloWorld has two states, namely waiting to connect to the socket
// and already connected to the socket
enum HelloWorld {
Connecting(ConnectFuture),
Connected(TcpStream, Cursor<Bytes>),
}
impl Future for HelloWorld {
type Item = ();
type Error = io::Error;
fn poll(&mut self) -> Poll<(), io::Error> {
use self::HelloWorld::*;
loop {
let socket = match *self {
Connecting(ref mut f) => {
try_ready!(f.poll())
}
Connected(ref mut socket, ref mut data) => {
// Keep trying to write the buffer to the socket as long as the
// buffer has more bytes it available for consumption
while data.has_remaining() {
try_ready!(socket.write_buf(data));
}
return Ok(Async::Ready(()));
}
};
let data = Cursor::new(Bytes::from_static(b"hello world"));
*self = Connected(socket, data);
}
}
}
fn main() {
let addr = "127.0.0.1:1234".parse().unwrap();
let connect_future = TcpStream::connect(&addr);
let hello_world = HelloWorld::Connecting(connect_future);
// Run it
tokio::run(hello_world)
}
The compiler outputs error messages:
error[E0271]: type mismatch resolving `<HelloWorld as futures::Future>::Error == ()`
--> src\main.rs:54:5
|
54 | tokio::run(hello_world)
| ^^^^^^^^^^ expected struct `std::io::Error`, found ()
|
= note: expected type `std::io::Error`
found type `()`
= note: required by `tokio::run`
Is the problem caused by the version of my Rust compiler? How do I fix it?
Tokio::run has the following signature:
pub fn run<F>(future: F)
where
F: Future<Item = (), Error = ()> + Send + 'static,
which means that it accepts a Future which accepts a () as Item and has () as the error type.
On the other hand, your HelloWorld impl has
type Item = ();
type Error = io::Error;
which means they are not compatible, you have to convert the io::Error to () somehow.
I would suggest using map_err
tokio::run(hello_world.map_err(|e| Err(e).unwrap()))
to handle the error in case something bad happens. You could of course make better error handling, but this will work.
Interestingly enough, I have JavaScript disabled in my browser and therefore I see the comments in the Rustdoc on the webpage:
fn main() {
let addr = "127.0.0.1:1234".parse().unwrap();
let connect_future = TcpStream::connect(&addr);
let hello_world = HelloWorld::Connecting(connect_future);
# let hello_world = futures::future::ok::<(), ()>(());
// Run it
tokio::run(hello_world)
}
The # means that Rustdoc should not print that line, but should execute it while testing. I think this is a mistake/oversight and there is also an open issue and a fix pending. PR has been merged, webpage has been updated.

No method named `post` found for type `hyper::Client` in Hyper 0.11

I want use to Hyper for crafting HTTP requests. Calling Client::get works fine but other methods such as Client::post and Client::head cause an compilation error.
extern crate futures;
extern crate hyper;
extern crate tokio_core;
use std::io::{self, Write};
use futures::{Future, Stream};
use hyper::Client;
use tokio_core::reactor::Core;
fn main() {
let mut core = Core::new().unwrap();
let client = Client::new(&core.handle());
let uri = "http://httpbin.org/ip".parse().unwrap();
let work = client.post(uri).and_then(|res| {
// if post changed to get it will work correctly
println!("Response: {}", res.status());
res.body("x=z")
.for_each(|chunk| io::stdout().write_all(&chunk).map_err(From::from))
});
core.run(work).unwrap();
}
error:
error[E0599]: no method named `post` found for type `hyper::Client<hyper::client::HttpConnector>` in the current scope
--> src/main.rs:15:23
|
15 | let work = client.post(uri).and_then(|res| {
| ^^^^
error[E0277]: the trait bound `[u8]: std::marker::Sized` is not satisfied
--> src/main.rs:20:24
|
20 | .for_each(|chunk| io::stdout().write_all(&chunk).map_err(From::from))
| ^^^^^ `[u8]` does not have a constant size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `[u8]`
= note: all local variables must have a statically known size
There's no secret trickery to the error message. You are getting the error "no method named post found for type hyper::Client" because there's no such method.
If you review the documentation for Client, you can see all the methods it has. None of them are post.
Instead, you need to use Client::request and pass in a Request value. The constructor for Request accepts a Method which denotes the HTTP method to use.
use hyper::{Client, Request, Method};
fn main() {
// ...
let uri = "http://httpbin.org/ip".parse().unwrap();
let req = Request::new(Method::Post, uri);
let work = client.request(req).and_then(|res| {
// ...
});
}
The crate documentation says:
If just starting out, check out the Guides first.
There is a guide for exactly your case: Advanced Client Usage.

Resources