期货 - 预期 (),找到结构 futures::Map

Futures - expected (), found struct futures::Map

我正在尝试使用 Hyper, then deserialize it via JSON through Serde 发送请求,但我似乎无法完全理解 futures 并且我收到类型不匹配错误,指出 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;
}

我完全迷失了,我认为此时任何建议都会有所帮助。

您在链接 futures 时有多个错误源于逻辑问题。我已经修复了它的实现,它是 available on the playground,但我强烈建议您和我一起了解我所做的更改。

但首先,您的代码中有一个反复出现的趋势:

#1:Future::map() 的 return 类型

Future::map() 允许您将类型 T 的未来结果更改为类型 R,并且假设 转换不会失败(即 Fn(T) -> R)。您在代码中多次使用 map(),同时 return 使用另一个 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。在非期货世界中,这可能会通过,但在需要拥有 return 对象的情况下(因此会出现生命周期错误)。

for<'t> Deserialize<'t> 是一个完全不同的野兽,它告诉编译器这个特征将有生命周期 't,但随后将成为一个拥有的对象,或者换句话说,使用的切片创建对象需要在整个生命周期 't 中存在,而不是整个 returned 对象。正是我们所需要的!

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