如何 return 一个带有 `&self` 的未来组合子
How to return a future combinator with `&self`
我有这段使用 futures v0.1 的代码:
impl ArcService for (Box<MiddleWare<Request>>, Box<ArcService>) {
fn call(&self, req: Request, res: Response) -> Box<Future<Item = Response, Error = Error>> {
box self.0.call(req).and_then(move |req| self.1.call(req, res))
}
}
pub trait ArcService: Send + Sync {
fn call(&self, req: Request, res: Response) -> Box<Future<Item = Response, Error = Error>>;
}
pub trait MiddleWare<T>: Sync + Send {
fn call<'a>(&'a self, param: T) -> Box<Future<Item = T, Error = Error> + 'a>;
}
type MiddleWareFuture<'a, I> = Box<Future<Item = I, Error = Error> + 'a>;
impl MiddleWare<Request> for Vec<Box<MiddleWare<Request>>> {
fn call(&self, request: Request) -> MiddleWareFuture<Request> {
self.iter()
.fold(box Ok(request).into_future(), |request, middleware| {
box request.and_then(move |req| middleware.call(req))
})
}
}
pub struct ArcRouter {
routes: HashMap<Method, Box<ArcService>>,
}
// Service implementation
impl hyper::Server::Service for ArcRouter {
type Response = Response;
type Request = Request;
type Error = hyper::Error;
type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;
fn call(&self, req: Request) -> Box<Future<Item = Self::Response, Error = Self::Error>> {
if let Some(routeMatch) = self.matchRoute(req.path(), req.method()) {
let mut request: ArcRequest = req.into();
request.paramsMap.insert(routeMatch.params);
let response = routeMatch.handler //handler is ArcService
.call(request, ArcResponse::new())
.map(|res| res.into());
return box response;
}
// TODO: this should be handled by a user defined 404 handler
return box Ok(Response::new().with_status(StatusCode::NotFound)).into_future();
}
}
请注意 Middleware
上的生命周期参数 — 它用于避免生命周期问题。
这不会编译,因为 Box<Future<Item = Response, Error = Error>>
是隐式的 'static
,因此会导致生命周期问题。 hyper::Server::Service
需要 'static Future
这是一个恰当地描述了我的问题的例子:
extern crate futures; // v0.1 (old)
use futures::{future, Future};
struct Example {
age: i32,
}
// trait is defined in an external crate. You can't change it's definition
trait MakeFuture {
fn make_a_future(&self) -> Box<Future<Item = i32, Error = ()>>;
}
impl MakeFuture for Example {
fn make_a_future(&self) -> Box<Future<Item = i32, Error = ()>> {
let f = future::ok(self).map(|ex| ex.age + 1);
Box::new(f)
}
}
给出生命周期错误:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/main.rs:16:28
|
16 | let f = future::ok(self).map(|ex| ex.age + 1);
| ^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 15:5...
--> src/main.rs:15:5
|
15 | / fn make_a_future(&self) -> Box<Future<Item = i32, Error = ()>> {
16 | | let f = future::ok(self).map(|ex| ex.age + 1);
17 | | Box::new(f)
18 | | }
| |_____^
note: ...so that expression is assignable (expected &Example, found &Example)
--> src/main.rs:16:28
|
16 | let f = future::ok(self).map(|ex| ex.age + 1);
| ^^^^
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that expression is assignable (expected std::boxed::Box<futures::Future<Item=i32, Error=()> + 'static>, found std::boxed::Box<futures::Future<Item=i32, Error=()>>)
--> src/main.rs:17:9
|
17 | Box::new(f)
| ^^^^^^^^^^^
有办法解决这个问题吗?我正在使用 hyper::Service
构建并使用 Rust v1.25.0(每晚)
How to return a future combinator with &self
你 return 指代 self
的未来是这样的:
use futures::future::{self, FutureResult}; // 0.1.28
struct Example {
age: i32,
}
impl Example {
fn make_a_future(&self) -> FutureResult<&Example, ()> {
future::ok(self)
}
}
正如 Tokio documentation on returning futures 中所讨论的,return 复杂未来的最简单稳定解决方案是 impl Trait。请注意,我们为 self
分配了一个明确的生命周期,并在 returned 值中使用它(通过 + 'a
):
use futures::{future, Future}; // 0.1.28
struct Example {
age: i32,
}
impl Example {
fn make_a_future<'a>(&'a self) -> impl Future<Item = i32, Error = ()> + 'a {
future::ok(self).map(|ex| ex.age + 1)
}
}
你的真实问题是"how can I lie to the compiler and attempt to introduce memory unsafety into my program?"
Box<SomeTrait + 'static>
(或 Box<SomeTrait>
本身)意味着特征对象 不得 包含任何不会持续整个程序的引用。根据定义,您的 Example
结构的生命周期比那个短。
这与期货无关。这是一个基本的 Rust 概念。
有很多关于线程的问题,它们有类似的限制。小样本:
就像在那些情况下一样,您正试图与可能存在的东西共享对变量的引用在变量被销毁之后。 C 或 C++ 等语言 允许您执行此操作 ,只是为了让您的程序在未来某个看似随机的时间点崩溃,当该变量在被释放后被访问时。顺便说一下,崩溃是 good 的情况;信息泄露或代码执行也是有可能的。
与线程的情况一样,您必须确保不会发生这种情况。最简单的方法是将变量移到未来,而不是共享它。另一种选择是在变量周围使用类似 Arc
的东西,克隆 Arc
并将克隆交给未来。
我有这段使用 futures v0.1 的代码:
impl ArcService for (Box<MiddleWare<Request>>, Box<ArcService>) {
fn call(&self, req: Request, res: Response) -> Box<Future<Item = Response, Error = Error>> {
box self.0.call(req).and_then(move |req| self.1.call(req, res))
}
}
pub trait ArcService: Send + Sync {
fn call(&self, req: Request, res: Response) -> Box<Future<Item = Response, Error = Error>>;
}
pub trait MiddleWare<T>: Sync + Send {
fn call<'a>(&'a self, param: T) -> Box<Future<Item = T, Error = Error> + 'a>;
}
type MiddleWareFuture<'a, I> = Box<Future<Item = I, Error = Error> + 'a>;
impl MiddleWare<Request> for Vec<Box<MiddleWare<Request>>> {
fn call(&self, request: Request) -> MiddleWareFuture<Request> {
self.iter()
.fold(box Ok(request).into_future(), |request, middleware| {
box request.and_then(move |req| middleware.call(req))
})
}
}
pub struct ArcRouter {
routes: HashMap<Method, Box<ArcService>>,
}
// Service implementation
impl hyper::Server::Service for ArcRouter {
type Response = Response;
type Request = Request;
type Error = hyper::Error;
type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;
fn call(&self, req: Request) -> Box<Future<Item = Self::Response, Error = Self::Error>> {
if let Some(routeMatch) = self.matchRoute(req.path(), req.method()) {
let mut request: ArcRequest = req.into();
request.paramsMap.insert(routeMatch.params);
let response = routeMatch.handler //handler is ArcService
.call(request, ArcResponse::new())
.map(|res| res.into());
return box response;
}
// TODO: this should be handled by a user defined 404 handler
return box Ok(Response::new().with_status(StatusCode::NotFound)).into_future();
}
}
请注意 Middleware
上的生命周期参数 — 它用于避免生命周期问题。
这不会编译,因为 Box<Future<Item = Response, Error = Error>>
是隐式的 'static
,因此会导致生命周期问题。 hyper::Server::Service
需要 'static Future
这是一个恰当地描述了我的问题的例子:
extern crate futures; // v0.1 (old)
use futures::{future, Future};
struct Example {
age: i32,
}
// trait is defined in an external crate. You can't change it's definition
trait MakeFuture {
fn make_a_future(&self) -> Box<Future<Item = i32, Error = ()>>;
}
impl MakeFuture for Example {
fn make_a_future(&self) -> Box<Future<Item = i32, Error = ()>> {
let f = future::ok(self).map(|ex| ex.age + 1);
Box::new(f)
}
}
给出生命周期错误:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/main.rs:16:28
|
16 | let f = future::ok(self).map(|ex| ex.age + 1);
| ^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 15:5...
--> src/main.rs:15:5
|
15 | / fn make_a_future(&self) -> Box<Future<Item = i32, Error = ()>> {
16 | | let f = future::ok(self).map(|ex| ex.age + 1);
17 | | Box::new(f)
18 | | }
| |_____^
note: ...so that expression is assignable (expected &Example, found &Example)
--> src/main.rs:16:28
|
16 | let f = future::ok(self).map(|ex| ex.age + 1);
| ^^^^
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that expression is assignable (expected std::boxed::Box<futures::Future<Item=i32, Error=()> + 'static>, found std::boxed::Box<futures::Future<Item=i32, Error=()>>)
--> src/main.rs:17:9
|
17 | Box::new(f)
| ^^^^^^^^^^^
有办法解决这个问题吗?我正在使用 hyper::Service
构建并使用 Rust v1.25.0(每晚)
How to return a future combinator with
&self
你 return 指代 self
的未来是这样的:
use futures::future::{self, FutureResult}; // 0.1.28
struct Example {
age: i32,
}
impl Example {
fn make_a_future(&self) -> FutureResult<&Example, ()> {
future::ok(self)
}
}
正如 Tokio documentation on returning futures 中所讨论的,return 复杂未来的最简单稳定解决方案是 impl Trait。请注意,我们为 self
分配了一个明确的生命周期,并在 returned 值中使用它(通过 + 'a
):
use futures::{future, Future}; // 0.1.28
struct Example {
age: i32,
}
impl Example {
fn make_a_future<'a>(&'a self) -> impl Future<Item = i32, Error = ()> + 'a {
future::ok(self).map(|ex| ex.age + 1)
}
}
你的真实问题是"how can I lie to the compiler and attempt to introduce memory unsafety into my program?"
Box<SomeTrait + 'static>
(或 Box<SomeTrait>
本身)意味着特征对象 不得 包含任何不会持续整个程序的引用。根据定义,您的 Example
结构的生命周期比那个短。
这与期货无关。这是一个基本的 Rust 概念。
有很多关于线程的问题,它们有类似的限制。小样本:
就像在那些情况下一样,您正试图与可能存在的东西共享对变量的引用在变量被销毁之后。 C 或 C++ 等语言 允许您执行此操作 ,只是为了让您的程序在未来某个看似随机的时间点崩溃,当该变量在被释放后被访问时。顺便说一下,崩溃是 good 的情况;信息泄露或代码执行也是有可能的。
与线程的情况一样,您必须确保不会发生这种情况。最简单的方法是将变量移到未来,而不是共享它。另一种选择是在变量周围使用类似 Arc
的东西,克隆 Arc
并将克隆交给未来。