期货-预期(),找到结构期货::地图

时间:2019-09-17 02:59:53

标签: rust serde hyper

我正在尝试使用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;
}

我完全迷失了方向,我认为目前任何建议都可以帮忙。

1 个答案:

答案 0 :(得分:5)

在链接期货时,由于逻辑问题会产生多个错误。我已经修复了它的实现,它是available on the playground,但我强烈建议您与我一起逐步了解我所做的更改。

但是首先,代码中的重复趋势:

#1:Future::map()的返回类型

Future::map()允许您将类型T的将来结果更改为类型R,并假设转换不会失败(即Fn(T) -> R)。您在代码中多次使用map(),同时返回另一个FutureResult。两者都不正确。

对于Future链接,and_then()允许您在错误类型保持相同的情况下执行映射fn(T) -> IntoFuture<Item = R>

对于Result,将它们通过future::result()转换为已经执行的未来,因此您也可以and_then()

#2:错误

错误不会自行转换,尤其是如果您未定义其转换方法,则不会自动转换。为此,我为您的错误类型实现了From<hyper::Error>

impl From<hyper::Error> for JsonError {
    fn from(s: hyper::Error) -> JsonError {
        JsonError::RequestError(s)
    }
}

这使您可以在类型检查器发现有可能使用的任何地方使用into()

但是请注意,由于hyper自己的响应类型,err_into()使类型检查器感到困惑,因此显式map(|r| r.into())

#3:Deserialize和生存期

Deserialize<'t>并不是您想要的确切特征。此特征表示整个对象需要生存一生't。在非未来世界中,这可能会过去,但是在这种情况下,必须拥有返回对象(因此会出现生命周期错误)。

for<'t> Deserialize<'t>是完全不同的野兽,它告诉编译器此特征将具有生存期't,但是将成为拥有的对象,或者换句话说,就是用于创建的切片该对象将需要生存't,而不是整个返回的对象。正是我们所需要的!

另一个nitpick:您确实应该在此函数中将响应解析与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)))
            }
        },
    )
}