我试图用Hyper发送一个请求,然后通过Serde通过JSON反序列化它,但是我似乎无法束手无策,我收到了类型不匹配错误的说明expected (), found struct [put some odd struct here]。我也无法绕过每次更改都会吐出的令人难以置信的冗长而令人困惑的错误消息。这是我的代码:
extern crate futures;
extern crate hyper;
extern crate serde;
extern crate serde_json;
use futures::{
Future,
Stream,
future
};
use hyper::{
Body,
Client,
Response,
StatusCode,
Uri,
client::HttpConnector,
};
use serde::{ Deserialize };
use std::error::{ Error };
enum JsonError
{
RequestError(hyper::Error),
ResponseError(StatusCode),
DeserializeError(serde_json::Error),
}
fn get_json
<'t, T, F>
(client: &Client<HttpConnector>, uri: Uri)
-> impl Future<Item = T, Error = JsonError>
where
T : Deserialize<'t>
{
let f = client
.get(uri)
.map(|response|
{
let (parts, body) = response.into_parts();
if parts.status.is_success()
{
body
.fold(
vec![],
|mut accum, chunk|
{
accum.extend_from_slice(&*chunk);
Ok(accum)
}
)
.map(|v|
{
serde_json::from_slice::<T>(&v)
.map_err(|err| JsonError::DeserializeError(err))
})
}
future::err(JsonError::ResponseError(parts.status))
})
.map_err(|err| JsonError::RequestError(err));
return f;
}
Run Code Online (Sandbox Code Playgroud)
我完全迷失了,我认为任何建议都可以在这一点上有所帮助。
链接期货时,由于逻辑问题会产生多个错误。我已经修复了它的实现,并且可以在操场上找到它,但是我强烈建议您与我一起了解我所做的更改。
但是首先,代码中的重复趋势:
Future::map()的返回类型Future::map()允许您将type的将来结果更改T为type R,并且前提是转换不会失败(即Fn(T) -> R)。您map()在代码中使用了多次,同时返回另一个Future或一个Result。两者都不正确。
对于Future链接,and_then()允许您在fn(T) -> IntoFuture<Item = R>错误类型保持相同的情况下执行映射
对于Result,将它们future::result()转换为已经执行的未来,因此您也可以and_then()
错误不会自行转换,尤其是如果您未定义错误的转换方法,则不会自动转换。为此,我From<hyper::Error>为您的错误类型实现了:
impl From<hyper::Error> for JsonError {
fn from(s: hyper::Error) -> JsonError {
JsonError::RequestError(s)
}
}
Run Code Online (Sandbox Code Playgroud)
这使您可以into()在类型检查器发现有可能的任何地方使用它。
但是要注意,由于hyper自身的响应类型,err_into()会使类型检查器感到困惑,因此是显式的map(|r| r.into())。
Deserialize和一生Deserialize<'t>不是您要寻找的确切特征。这种特质意味着整个物体需要终生存在't。在非未来世界中,这可能会过去,但是在这种情况下,必须拥有返回对象(因此,您将获得生命周期错误),这不会通过。
for<'t> Deserialize<'t>是完全不同的野兽,并告诉编译器此特征将具有生存期't,但随后将成为拥有的对象,换句话说,用于创建该对象的切片将需要生存期't,而不是整个返回期宾语。正是我们需要的!
另外一个选择:您确实应该在此函数中将响应解析与HTTP提取分开。就目前而言,如果我通过HTTPS发出请求,则将无法使用您的get_json()函数,因为我的连接器hyper将是TlsConnector<HttpConnector>。有问题的;-)
代码:
use futures::{future, Future, Stream};
use hyper::{client::HttpConnector, Client, StatusCode, Uri};
use serde::Deserialize;
enum JsonError {
RequestError(hyper::Error),
ResponseError(StatusCode),
DeserializeError(serde_json::Error),
}
impl From<hyper::Error> for JsonError {
fn from(s: hyper::Error) -> JsonError {
JsonError::RequestError(s)
}
}
fn get_json<T, F>(
client: &Client<HttpConnector>,
uri: Uri,
) -> impl Future<Item = T, Error = JsonError>
where
T: for<'t> Deserialize<'t> + 'static,
{
client.get(uri).map_err(|e| e.into()).and_then(
|response| -> Box<dyn Future<Item = T, Error = JsonError>> {
let (parts, body) = response.into_parts();
match parts.status.is_success() {
true => Box::new(body
.map_err(|e| e.into())
.fold(
vec![],
|mut accum, chunk| -> Box<dyn Future<Item = Vec<u8>, Error = JsonError>>
{
accum.extend_from_slice(&*chunk);
Box::new(future::ok(accum))
}
)
.and_then(|v|
{
future::result(serde_json::from_slice::<T>(&v))
.map_err(|err| JsonError::DeserializeError(err))
})),
false => Box::new(future::err(JsonError::ResponseError(parts.status)))
}
},
)
}
Run Code Online (Sandbox Code Playgroud)