如何减少终身冲突?

时间:2018-02-19 07:21:25

标签: rust lifetime

我害怕终身问题。我搜索并获得了如此多的相关结果。我觉得它们之间有如此不同,我认为很难从中获得灵魂。所以我决定问。

在Hyper服务的调用中调用dao方法时发生错误,我无法修复它!

extern crate futures;
extern crate hyper;

use futures::future::Future;
use futures::Stream;
use hyper::server::{Http, Request, Response, Service};
use hyper::StatusCode;
use std::net::SocketAddr;

trait Dao {
    fn input_data(&self, data: &str) -> bool;
}

struct MysqlDao;

impl Dao for MysqlDao {
    fn input_data(&self, data: &str) -> bool {
        unimplemented!()
    }
}

struct HelloWorld<'a> {
    dao: &'a Dao,
}

impl<'a> Service for HelloWorld<'a> {
    type Request = Request;
    type Response = Response;
    type Error = hyper::Error;
    type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;

    fn call(&self, req: Request) -> Self::Future {
        Box::new(req.body().concat2().map(|b| {
            let rtn = self.dao.input_data(std::str::from_utf8(b.as_ref()).unwrap());
            let rtn = true; // line 35 this is ok
            match rtn {
                true => {
                    return Response::new()
                        .with_status(StatusCode::Ok)
                        .with_body(String::from("ok"));
                }
                false => {
                    return Response::new()
                        .with_status(StatusCode::UnprocessableEntity)
                        .with_body(String::from("error"));
                }
            }
        }))
    }
}

fn main() {
    let addr = "127.0.0.1:3000".parse().unwrap();
    static DAO: MysqlDao = MysqlDao;
    web_startup(&addr, &DAO);
}

fn web_startup<T: Dao>(addr: &SocketAddr, dao: &'static T) {
    let server = Http::new()
        .bind(addr, move || Ok(HelloWorld { dao }))
        .unwrap();
    server.run().unwrap();
}

playground

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src/main.rs:33:9
   |
33 | /         Box::new(req.body().concat2().map(|b| {
34 | |             let rtn = self.dao.input_data(std::str::from_utf8(b.as_ref()).unwrap());
35 | |             let rtn = true; // line 35 this is ok
36 | |             match rtn {
...  |
47 | |             }
48 | |         }))
   | |___________^
   |
note: first, the lifetime cannot outlive the lifetime 'a as defined on the impl at 26:1...
  --> src/main.rs:26:1
   |
26 | / impl<'a> Service for HelloWorld<'a> {
27 | |     type Request = Request;
28 | |     type Response = Response;
29 | |     type Error = hyper::Error;
...  |
49 | |     }
50 | | }
   | |_^
note: ...so that the type `futures::Map<futures::stream::Concat2<hyper::Body>, [closure@src/main.rs:33:43: 48:10 self:&&HelloWorld<'a>]>` will meet its required lifetime bounds
  --> src/main.rs:33:9
   |
33 | /         Box::new(req.body().concat2().map(|b| {
34 | |             let rtn = self.dao.input_data(std::str::from_utf8(b.as_ref()).unwrap());
35 | |             let rtn = true; // line 35 this is ok
36 | |             match rtn {
...  |
47 | |             }
48 | |         }))
   | |___________^
   = note: but, the lifetime must be valid for the static lifetime...
note: ...so that expression is assignable (expected std::boxed::Box<futures::Future<Error=hyper::Error, Item=hyper::Response> + 'static>, found std::boxed::Box<futures::Future<Error=hyper::Error, Item=hyper::Response>>)
  --> src/main.rs:33:9
   |
33 | /         Box::new(req.body().concat2().map(|b| {
34 | |             let rtn = self.dao.input_data(std::str::from_utf8(b.as_ref()).unwrap());
35 | |             let rtn = true; // line 35 this is ok
36 | |             match rtn {
...  |
47 | |             }
48 | |         }))
   | |___________^

问题行是34.当我用第35行替换第34行时,它可以工作。

2 个答案:

答案 0 :(得分:1)

在这段代码中,生命周期中发生了一些事情,错误信息并不是很好。

首先,您的call函数返回特征对象;也就是说,Box<T>其中T trait 而不是具体类型。特征对象的默认生命周期为'static,因此Box<Future>等同于Box<Future + 'static>。这解释了这一消息的来源:

note: ...so that expression is assignable (expected std::boxed::Box<futures::Future<Error=hyper::Error, Item=hyper::Response> + 'static>, found std::boxed::Box<futures::Future<Error=hyper::Error, Item=hyper::Response>>)

在这种情况下,您不需要返回的Future生效'static。相反,您希望它与self.dao具有相同的生命周期,因此请更改您的类型别名,如下所示:

type Future = Box<'a + Future<Item = Self::Response, Error = Self::Error>>;

第二件事是你的闭包捕获的变量。你的闭包是指self,所以它需要保存那个引用。这意味着关闭不能超过方法的结束 - 但是您已经指定它只会活'a;也就是说,它至少与self.dao一样长。

请注意,此方法中的self 的生命周期为'aHelloWorld的实例可能比其引用的dao的生命周期更短。

要解决此问题的一半,您需要允许闭包只捕获 self.dao而不捕获self。所以你必须添加

let dao = self.dao;
在关闭之前

,然后在闭包内引用dao而不是self.dao,以便它不会捕获self。您还需要使闭包成为move闭包,否则它最终会捕获对引用的引用,这仍将生命周期与函数的范围联系起来。

最初在评论中发布此建议的用户red75prime提出a playground with these changes。第30,33和34行是重要的。

答案 1 :(得分:0)

我找到了一种方法,但我认为这很难看。

dao移除HelloWorld属性;将Sync添加到特征Dao;将DAO移出到上限范围;直接从DAO调用fn call

代码:playground