I'm working on some code whose structure is similar to that of "middlewares" in web frameworks, so I'll use that terminology in this minimal example. The idea of a middleware function is that it wraps the actual response handler, so it can modify the request before it goes to the handler, and modify the response after it comes back:
struct Request;
struct Response;
trait Handler {
fn handle(&self, request: Request) -> Response;
}
trait Middleware {
fn handle(&self, request: Request, next: Box<dyn FnOnce(Request) -> Response>) -> Response;
}
I considered having two separate functions, one to preprocess the request and one to postprocess the response, but then I wouldn't be able to store any additional state during the request without resorting to hacks.
The server contains dynamically configured handlers and middlewares, so it has to use some boxed trait objects:
struct Server {
handler: Box<dyn Handler>,
middlewares: Vec<Box<dyn Middleware>>,
}
Now, how to implement the response handling? Here are my two attempts:
impl Server {
// First attempt: using Iterator::fold()
fn handle_request_fold<'a>(&'a self, request: Request) -> Response {
let handler_with_middlewares = self.middlewares.iter()
.rev()
.fold::<Box<dyn FnOnce(Request) -> Response + 'a>, _>(
Box::new(|request| self.handler.handle(request)),
|next, middleware| {
Box::new(|request| middleware.handle(request, next))
}
);
handler_with_middlewares(request)
}
// Second attempt: using recursion
fn handle_request_recurse(&self, request: Request) -> Response {
self.handle_request_from(request, 0)
}
fn handle_request_from<'a>(&'a self, request: Request, index: usize) -> Response {
if index >= self.middlewares.len() {
self.handler.handle(request)
} else {
let next = Box::new(
|r: Request| self.handle_request_from(r, index + 1));
self.middlewares[index].handle(request, next)
}
}
}
Both attempts give the same error, which suggests that I'm doing something fundamentally wrong here:
error[E0759]: `self` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement
--> src/lib.rs:25:67
|
19 | fn handle_request_fold<'a>(&'a self, request: Request) -> Response {
| -------- this data with lifetime `'a`...
...
25 | Box::new(|request| middleware.handle(request, next))
| ^^^^ ...is captured and required to live as long as `'static` here
I know that trait objects have an implicit 'static lifetime, so I tried adding explicit lifetimes as you can see, but it didn't help. I don't understand why the compiler would demand a 'static lifetime anywhere here; nothing (including the closures) can escape the handle_request_* functions, right?