I'm trying to refactor code by moving the hyper server out of main and allow the struct function to accept a function as below:
// main.rs
#[tokio::main]
async fn main() {
let listener = Listener::new(String::from("127.0.0.1"), String::from("3000"));
listener.start(request_handler);
}
async fn request_handler(req: Request<Body>) -> ConfigResult<Response<Body>> {
match req.method() {
&Method::GET => get(req.body()),
_ => Ok(Response::builder()
.status(StatusCode::NOT_FOUND)
.body("NOT FOUND".into())
.unwrap())
}
}
// lib.rs
pub type ConfigError = Box<dyn Error + Send + Sync>;
pub type ConfigResult<T> = Result<T, Box<dyn Error + Send + Sync>>;
#[derive(Debug, Clone)]
pub struct Listener(pub SocketAddr);
impl<'a> Listener {
pub fn new(addr: String, port:String) -> Self {
let ip = IpAddr::from_str(addr.as_str()).unwrap();
let address = SocketAddr::new(ip, port.parse::<u16>().unwrap());
Listener(address)
}
pub async fn start<F>(&'a self, request_handler: F ) -> ConfigResult<()>
where F: Fn(Request<Body>) -> ConfigResult<Response<Body>> + Sync + 'a
{
let mut service_closure = move |req| {
async {
request_handler(req)
}
};
let mut make_service_closure = move |_| {
async {
Ok::<_, ConfigError>(service_fn(&service_closure))
}
};
let service = make_service_fn(make_service_closure);
let server = Server::bind(&self.0).serve(service);
server.await?;
Ok(())
}
}
Im getting the error:
error: implementation of `FnOnce` is not general enough
--> src/config/server.rs:36:15
|
36 | server.await?;
| ^^^^^^ implementation of `FnOnce` is not general enough
|
= note: closure with signature `fn(&'2 AddrStream) -> impl Future<Output = Result<hyper::service::util::ServiceFn<&[closure#src/config/serva.rs:23:35: 27:10], Body>, Box<dyn StdError + Send + Sync>>>` must implement `FnOnce<(&'1 AddrStream,)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&'2 AddrStream,)>`, for some specific lifetime `'2`
Looking at the error above, my conclusion is its a lifetime error, where let make_service_closure = move |_| {...} requires lifetime of '1 but when i try let make_service_closure = move | a: &'a AddrStream| {...} i get lifetime mismatch error
expected type `for<'r> FnMut<(&'r AddrStream,)>`
found type `FnMut<(&'a AddrStream,)>`
plus the error implementation of FnOnce is not general enough. Please help understand which lifetime should i use for the hyper service function and why is FnOnce is not general enough.
Related
I have the following code:
use tokio; // 1.7.1
use futures::future::Future;
use std::pin::Pin;
use futures::FutureExt;
use std::marker::PhantomData;
use std::marker::Send;
use std::sync::Arc;
use async_trait::async_trait;
struct Orchestrator {
worker: Box<dyn WorkerT<Box<dyn Fn(i32) -> Pin<Box<dyn Future<Output = i32> + Send>> + Send + Sync>> + Send + Sync>
}
impl Orchestrator {
async fn other_things(&self, num: i32) -> i32{
// Do some async stuff in here
num+1
}
async fn main_stuff(self: Arc<Self>) {
let func = |num: i32| {
let slf = self.clone();
async move {
slf.other_things(num).await
}.boxed()
};
self.worker.do_work(Box::new(func)).await;
}
}
#[async_trait]
trait WorkerT<F: Fn(i32) -> Pin<Box<dyn Future<Output = i32> + Send>> + Send + Sync> {
async fn do_work(& self, func: F);
}
struct Worker<F: Fn(i32) -> Pin<Box<dyn Future<Output = i32> + Send>> + Send + Sync> {
phantom: std::marker::PhantomData<F>
}
#[async_trait]
impl<F: Fn(i32) -> Pin<Box<dyn Future<Output = i32> + Send>> + Send + Sync> WorkerT<F> for Worker<F> {
async fn do_work(& self, func: F) {
for i in 0..5 {
func(i).await;
}
}
}
#[tokio::main]
async fn main() {
let orchestrator = Arc::new(Orchestrator { worker: Box::new(Worker { phantom: PhantomData }) });
orchestrator.main_stuff().await;
}
With the following error:
error[E0597]: `self` does not live long enough
--> src/main.rs:22:23
|
21 | let func = |num: i32| {
| ---------- value captured here
22 | let slf = self.clone();
| ^^^^ borrowed value does not live long enough
...
28 | self.worker.do_work(Box::new(func)).await;
| -------------- cast requires that `self` is borrowed for `'static`
29 | }
| - `self` dropped here while still borrowed
Currently, because the default lifetime of dyn WorkerT... is 'static, it requires that the borrow of self in main_stuff be 'static. I need to relax the lifetime of the worker field but I don't know how. If I change worker to be of type 'Workerinstead ofdyn WorkerT...` this works, but I would much prefer keep it as the trait.
I have tried adding a lifetime to the Orchestrator struct and then giving that lifetime to the worker field, but then it says that my created lifetime needs to outlive 'static.
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=7f34fa394d706409942659f1d8ce36c0
You need to clone the Arc before creating the closure so that you can give an Arc to the closure. What you have in your code tries to borrow self, which won't outlive the closure. Cloning the Arc solves this issue as the closure can now own its own Arc pointing at the same value.
For example:
async fn main_stuff(self: Arc<Self>) {
let self2 = self.clone();
let func = move |num: i32| {
let self3 = self2.clone();
async move {
self3.other_things(num).await
}.boxed()
};
self.worker.do_work(Box::new(func)).await;
}
The inner clone is still required so that the type of func implements Fn (otherwise it will implement FnOnce due to moving a captured variable into the future).
Solution
async fn main_stuff(self: Arc<Self>) {
let slf = self.clone();
let func = move |num: i32| {
let slf = slf.clone();
async move { slf.other_things(num).await }.boxed()
};
self.worker.do_work(Box::new(func)).await;
}
Explanation
You are passing a closure that in turns creates async routine. First, the closure it self is static, so it has to take ownership of self. We clone self to slf and add move to the closure, thus closure moved it. Then we have to clone the slf each time and let the async routine own it when it exits the closure.
I'm trying to implement a StateMachine in Rust, but I encountered some problems while trying to fire the callback of StateMachine in a spawn thread.
Here is my StateMachine struct. The state is a generic T because I want to use it in many different scenerios, and I use a Vec to store all the callbacks those registered into this StateMachine.
At the very begining, I didn't use the lifetime 'a, but it will run into some lifetime problems, so I add the lifetime 'a by this suggestion: Idiomatic callbacks in Rust
pub struct StateMachine<'a, T> where T:Clone+Eq+'a {
state: RwLock<T>,
listeners2: Vec<Arc<Mutex<ListenerCallback<'a, T>>>>,
}
pub type ListenerCallback<'a, T> = dyn FnMut(T) -> Result<()> + Send + Sync + 'a ;
When the state is changed, the StateMachine will fire all the callbacks, as follows.
pub async fn try_set(&mut self, new_state:T) -> Result<()> {
if (block_on(self.state.read()).deref().eq(&new_state)) {
return Ok(())
}
// todo change the state
// fire every listener in spawn
let mut fire_results = vec![];
for listener in &mut self.listeners2 {
let state = new_state.clone();
let fire_listener = listener.clone();
fire_results.push(tokio::spawn(async move {
let mut guard = fire_listener.lock().unwrap();
guard.deref_mut()(state);
}));
}
// if fire result return Err, return it
for fire_result in fire_results {
fire_result.await?;
}
Ok(())
}
But it will cause a compilation error.
error[E0521]: borrowed data escapes outside of associated function
--> src/taf/taf-core/src/execution/state_machine.rs:54:33
|
15 | impl<'a,T> StateMachine<'a,T> where T:Clone+Eq+Send {
| -- lifetime `'a` defined here
...
34 | pub async fn try_set(&mut self, new_state:T) -> Result<()> {
| --------- `self` is a reference that is only valid in the associated function body
...
54 | let fire_listener = listener.clone();
| ^^^^^^^^^^^^^^^^
| |
| `self` escapes the associated function body here
| argument requires that `'a` must outlive `'static`
##########################################################
The full code is coupled with a lot of business logic, so I rewrite 2 demos as follows, the problems is the same. The first demo fire callback synchronously and it works, the second demo try to fire callback asynchronously, it encounter the same problem: self escapes the associated function body here.
First demo(it works):
use std::alloc::alloc;
use std::ops::DerefMut;
use std::sync::{Arc, Mutex, RwLock};
use anyhow::Result;
use dashmap::DashMap;
struct StateMachine<'a,T> where T:Clone+Eq+'a {
state: T,
listeners: Vec<Box<Callback<'a, T>>>,
}
type Callback<'a, T> = dyn FnMut(T) -> Result<()> + Send + Sync + 'a;
impl<'a, T> StateMachine<'a,T> where T:Clone+Eq+'a {
pub fn new(init_state: T) -> Self {
StateMachine {
state: init_state,
listeners: vec![]
}
}
pub fn add_listener(&mut self, listener: Box<Callback<'a, T>>) -> Result<()> {
self.listeners.push(listener);
Ok(())
}
pub fn set(&mut self, new_state: T) -> Result<()> {
self.state = new_state.clone();
for listener in &mut self.listeners {
listener(new_state.clone());
}
Ok(())
}
}
#[derive(Clone, Eq, PartialEq, Hash)]
enum ExeState {
Waiting,
Running,
Finished,
Failed,
}
struct Execution<'a> {
exec_id: String,
pub state_machine: StateMachine<'a, ExeState>,
}
struct ExecManager<'a> {
all_jobs: Arc<RwLock<DashMap<String, Execution<'a>>>>,
finished_jobs: Arc<RwLock<Vec<String>>>,
}
impl<'a> ExecManager<'a> {
pub fn new() -> Self {
ExecManager {
all_jobs: Arc::new(RwLock::new(DashMap::new())),
finished_jobs: Arc::new(RwLock::new(vec![]))
}
}
fn add_job(&mut self, job_id: String) {
let mut execution = Execution {
exec_id: job_id.clone(),
state_machine: StateMachine::new(ExeState::Waiting)
};
// add listener
let callback_finished_jobs = self.finished_jobs.clone();
let callback_job_id = job_id.clone();
execution.state_machine.add_listener( Box::new(move |new_state| {
println!("listener fired!, job_id {}", callback_job_id.clone());
if new_state == ExeState::Finished || new_state == ExeState::Failed {
let mut guard = callback_finished_jobs.write().unwrap();
guard.deref_mut().push(callback_job_id.clone());
}
Ok(())
}));
let mut guard = self.all_jobs.write().unwrap();
guard.deref_mut().insert(job_id, execution);
}
fn mock_exec(&mut self, job_id: String) {
let mut guard = self.all_jobs.write().unwrap();
let mut exec = guard.deref_mut().get_mut(&job_id).unwrap();
exec.state_machine.set(ExeState::Finished);
}
}
#[test]
fn test() {
let mut manager = ExecManager::new();
manager.add_job(String::from("job_id1"));
manager.add_job(String::from("job_id2"));
manager.mock_exec(String::from("job_id1"));
manager.mock_exec(String::from("job_id2"));
}
Second demo:
use std::alloc::alloc;
use std::ops::DerefMut;
use std::sync::{Arc, Mutex, RwLock};
use anyhow::Result;
use dashmap::DashMap;
use petgraph::algo::astar;
struct StateMachine<'a,T> where T:Clone+Eq+Send+'a {
state: T,
listeners: Vec<Arc<Mutex<Box<Callback<'a, T>>>>>,
}
type Callback<'a, T> = dyn FnMut(T) -> Result<()> + Send + Sync + 'a;
impl<'a, T> StateMachine<'a,T> where T:Clone+Eq+Send+'a {
pub fn new(init_state: T) -> Self {
StateMachine {
state: init_state,
listeners: vec![]
}
}
pub fn add_listener(&mut self, listener: Box<Callback<'a, T>>) -> Result<()> {
self.listeners.push(Arc::new(Mutex::new(listener)));
Ok(())
}
pub fn set(&mut self, new_state: T) -> Result<()> {
self.state = new_state.clone();
for listener in &mut self.listeners {
let spawn_listener = listener.clone();
tokio::spawn(async move {
let mut guard = spawn_listener.lock().unwrap();
guard.deref_mut()(new_state.clone());
});
}
Ok(())
}
}
#[derive(Clone, Eq, PartialEq, Hash)]
enum ExeState {
Waiting,
Running,
Finished,
Failed,
}
struct Execution<'a> {
exec_id: String,
pub state_machine: StateMachine<'a, ExeState>,
}
struct ExecManager<'a> {
all_jobs: Arc<RwLock<DashMap<String, Execution<'a>>>>,
finished_jobs: Arc<RwLock<Vec<String>>>,
}
impl<'a> ExecManager<'a> {
pub fn new() -> Self {
ExecManager {
all_jobs: Arc::new(RwLock::new(DashMap::new())),
finished_jobs: Arc::new(RwLock::new(vec![]))
}
}
fn add_job(&mut self, job_id: String) {
let mut execution = Execution {
exec_id: job_id.clone(),
state_machine: StateMachine::new(ExeState::Waiting)
};
// add listener
let callback_finished_jobs = self.finished_jobs.clone();
let callback_job_id = job_id.clone();
execution.state_machine.add_listener( Box::new(move |new_state| {
println!("listener fired!, job_id {}", callback_job_id.clone());
if new_state == ExeState::Finished || new_state == ExeState::Failed {
let mut guard = callback_finished_jobs.write().unwrap();
guard.deref_mut().push(callback_job_id.clone());
}
Ok(())
}));
let mut guard = self.all_jobs.write().unwrap();
guard.deref_mut().insert(job_id, execution);
}
fn mock_exec(&mut self, job_id: String) {
let mut guard = self.all_jobs.write().unwrap();
let mut exec = guard.deref_mut().get_mut(&job_id).unwrap();
exec.state_machine.set(ExeState::Finished);
}
}
#[test]
fn test() {
let mut manager = ExecManager::new();
manager.add_job(String::from("job_id1"));
manager.add_job(String::from("job_id2"));
manager.mock_exec(String::from("job_id1"));
manager.mock_exec(String::from("job_id2"));
}
Compile error of second demo:
error[E0521]: borrowed data escapes outside of associated function
--> generic/src/callback2.rs:34:34
|
15 | impl<'a, T> StateMachine<'a,T> where T:Clone+Eq+Send+'a {
| -- lifetime `'a` defined here
...
29 | pub fn set(&mut self, new_state: T) -> Result<()> {
| --------- `self` is a reference that is only valid in the associated function body
...
34 | let spawn_listener = listener.clone();
| ^^^^^^^^^^^^^^^^
| |
| `self` escapes the associated function body here
| argument requires that `'a` must outlive `'static`
|
= note: requirement occurs because of the type `std::sync::Mutex<Box<dyn FnMut(T) -> Result<(), anyhow::Error> + Send + Sync>>`, which makes the generic argument `Box<dyn FnMut(T) -> Result<(), anyhow::Error> + Send + Sync>` invariant
= note: the struct `std::sync::Mutex<T>` is invariant over the parameter `T`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
Tasks spawned with tokio::spawn() cannot use borrowed data (here, the data with lifetime 'a, whatever it may be). This is because there is not currently (and likely will never be) any way to guarantee that the borrowed data reliably outlives the spawned task.
You have two choices:
Fire the notifications without spawning. You can put the notification futures into a FuturesUnordered to run them all concurrently, but they will still all have to finish before try_set() does.
Remove the lifetime parameter; stop allowing callbacks that borrow data. Put 'static on your dyn types where necessary. Change the users of the StateMachine so they do not try to use borrowed data but use Arc instead, if necessary.
pub struct StateMachine<T> where T: Clone + Eq + 'static {
state: RwLock<T>,
listeners2: Vec<Arc<Mutex<ListenerCallback<T>>>>,
}
pub type ListenerCallback<T> = dyn FnMut(T) -> Result<()> + Send + Sync + 'static;
I try to create a transaction object from a parent connection being in async context. Here is a distilled situation:
use std::fmt::Debug;
use std::future::Future as StdFuture;
use std::pin::Pin;
use std::result::Result as StdResult;
#[derive(Debug)]
enum Error {
Unknown,
}
type Result<T> = StdResult<T, Error>;
type Future<'a, T> = Pin<Box<dyn StdFuture<Output = T> + Send + 'a>>;
trait DataConn<'c> {
type Tx: DataTx<'c>;
fn begin(&'c mut self) -> Future<Result<Self::Tx>>;
}
trait DataTx<'c> {
type Tx: DataTx<'c>;
fn begin(&'c mut self) -> Future<Result<Self::Tx>>;
}
async fn test_transactions<'c>(mut conn: impl DataConn<'c> + 'c)
{
let tx = conn.begin().await.unwrap();
}
As result I get the following error:
error[E0597]: `conn` does not live long enough
--> src/lib.rs:29:14
|
27 | async fn test_transactions<'c>(mut conn: impl DataConn<'c> + 'c)
| -- lifetime `'c` defined here
28 | {
29 | let tx = conn.begin().await.unwrap();
| ^^^^^^^^^^^^
| |
| borrowed value does not live long enough
| argument requires that `conn` is borrowed for `'c`
30 | }
| - `conn` dropped here while still borrowed
I don't get why the connection is going to be dropped before the transaction we create, while both have the same 'c lifetime. What do I miss here?
UPDATE: Some suggestions work being standalone, but cause compilation errors for my trait implementation:
struct SqliteConn<'c> {
conn: sqlx::SqliteConnection,
phantom: PhantomData<&'c ()>,
}
pub struct SqliteTx<'c> {
tx: Transaction<'c, Sqlite>,
}
impl<'c> DataConn<'c> for SqliteConn<'c> {
type Tx = SqliteTx<'c>;
fn begin(&'c mut self) -> Future<Result<Self::Tx>> {
Box::pin(async move {
Ok(SqliteTx {
tx: Connection::begin(&mut self.conn).await?,
})
})
}
}
You just need to annotate whatever necessary, just over the transaction lifetime (don't mess with self lifetimes here):
use std::fmt::Debug;
use std::future::Future as StdFuture;
use std::pin::Pin;
use std::result::Result as StdResult;
#[derive(Debug)]
enum Error {
Unknown,
}
type Result<T> = StdResult<T, Error>;
type Future<'a, T> = Pin<Box<dyn StdFuture<Output = T> + Send + 'a>>;
trait DataConn<'t> {
type Tx: DataTx<'t>;
fn begin(&mut self) -> Future<Result<Self::Tx>>;
}
trait DataTx<'t> {
type Tx: DataTx<'t>;
fn begin(&self) -> Future<Result<Self::Tx>>;
}
async fn test_transactions<'t>(mut conn: impl DataConn<'t>)
{
let tx = conn.begin().await.unwrap();
}
Playground
I have this piece of code using 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();
}
}
Note the lifetime parameter on Middleware — it is used to avoid lifetime issues.
This does not compile because Box<Future<Item = Response, Error = Error>> is implicitly 'static and therefore causes lifetime issues. hyper::Server::Service requires a 'static Future
Here is an example that aptly describes my problem:
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)
}
}
playground link
which gives the lifetime error:
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)
| ^^^^^^^^^^^
Is there a way to get around this? I'm building with hyper::Service and using Rust v1.25.0 (nightly)
How to return a future combinator with &self
You return a future that refers to self like this:
use futures::future::{self, FutureResult}; // 0.1.28
struct Example {
age: i32,
}
impl Example {
fn make_a_future(&self) -> FutureResult<&Example, ()> {
future::ok(self)
}
}
As discussed in the Tokio documentation on returning futures, the easiest stable solution to returning a complicated future is a impl Trait. Note that we assign an explicit lifetime to self and use that in the returned value (via + '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)
}
}
Your real question is "how can I lie to the compiler and attempt to introduce memory unsafety into my program?"
Box<SomeTrait + 'static> (or Box<SomeTrait> by itself) means that the trait object must not contain any references that do not last for the entire program. By definition, your Example struct has a shorter lifetime than that.
This has nothing to do with futures. This is a fundamental Rust concept.
There are many questions that ask the same thing in regards to threads, which have similar restrictions. A small sampling:
Lifetime of variables passed to a new thread
How do I use static lifetimes with threads?
The compiler suggests I add a 'static lifetime because the parameter type may not live long enough, but I don't think that's what I want
Lifetime troubles sharing references between threads
Like in those cases, you are attempting to share a reference to a variable with something that may exist after the variable is destroyed. Languages such as C or C++ would let you do this, only to have your program crash at a seemingly random point in time in the future when that variable is accessed after being freed. The crash is the good case, by the way; information leaks or code execution is also possible.
Like the case for threads, you have to ensure that this doesn't happen. The easiest way is to move the variable into the future, not sharing it at all. Another option is to use something like an Arc around your variable, clone the Arc and hand the clone to the future.
I have this piece of code using 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();
}
}
Note the lifetime parameter on Middleware — it is used to avoid lifetime issues.
This does not compile because Box<Future<Item = Response, Error = Error>> is implicitly 'static and therefore causes lifetime issues. hyper::Server::Service requires a 'static Future
Here is an example that aptly describes my problem:
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)
}
}
playground link
which gives the lifetime error:
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)
| ^^^^^^^^^^^
Is there a way to get around this? I'm building with hyper::Service and using Rust v1.25.0 (nightly)
How to return a future combinator with &self
You return a future that refers to self like this:
use futures::future::{self, FutureResult}; // 0.1.28
struct Example {
age: i32,
}
impl Example {
fn make_a_future(&self) -> FutureResult<&Example, ()> {
future::ok(self)
}
}
As discussed in the Tokio documentation on returning futures, the easiest stable solution to returning a complicated future is a impl Trait. Note that we assign an explicit lifetime to self and use that in the returned value (via + '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)
}
}
Your real question is "how can I lie to the compiler and attempt to introduce memory unsafety into my program?"
Box<SomeTrait + 'static> (or Box<SomeTrait> by itself) means that the trait object must not contain any references that do not last for the entire program. By definition, your Example struct has a shorter lifetime than that.
This has nothing to do with futures. This is a fundamental Rust concept.
There are many questions that ask the same thing in regards to threads, which have similar restrictions. A small sampling:
Lifetime of variables passed to a new thread
How do I use static lifetimes with threads?
The compiler suggests I add a 'static lifetime because the parameter type may not live long enough, but I don't think that's what I want
Lifetime troubles sharing references between threads
Like in those cases, you are attempting to share a reference to a variable with something that may exist after the variable is destroyed. Languages such as C or C++ would let you do this, only to have your program crash at a seemingly random point in time in the future when that variable is accessed after being freed. The crash is the good case, by the way; information leaks or code execution is also possible.
Like the case for threads, you have to ensure that this doesn't happen. The easiest way is to move the variable into the future, not sharing it at all. Another option is to use something like an Arc around your variable, clone the Arc and hand the clone to the future.