0

I am using rust-aws-lambda project. My use case is with Lambda as an API Gateway Proxy. Additionally I am using closures as explained in this example, as I want to use shared resources such as clients and secret value (retrieved from AWS Secrets Manager) in my lambda code.

The issue I am struggling with for long time is how I can abstract the closures into another helper function. I want the function to return the outer closure in this case, for example. I have tryd but I cannot get Rust compiler to let me do this.

The both closures are within main function. Per the listed example, my inner closure is defined something like this:

let handler_func_closure = move |event: ApiProxyRequest| async move {
    ...
    Ok::<_, Error>(success_resp)
};

These are my relevant imports in the code:

use lambda_http::{service_fn, Body, Error, IntoResponse, Request as ApiProxyRequest, RequestExt};
use lambda_http::tower::util::ServiceFn;

I have below closure which I have defined in async main function in main.rs, which is working for me so far.

let outer_closure = move |event: ApiProxyRequest| async move {
    match handler_func_closure(event).await {
        Ok(s) => Ok(s.into_response()),
        Err(e) => Ok(http::Response::builder()
          .header(http::header::CONTENT_TYPE, "application/json")
          .status(400)
          .body(
            serde_json::to_string(&json!({"error": &err.to_string()}))
                .expect("unable to serialize serde_json::Value")
                .into(),
        )
          .expect("unable to build http::Response")),
    }
};

Where into_response() is satisfied by a struct implementing from trait IntoResponse.

So basically what I'm trying to do is make a function to return closure that can be passed in to service_fn. Below is my attempt so far - but it's not satisfy Rust compiler currently.

pub fn get_service_fn<T, T2, F, F2, I: IntoResponse>(handler_func_closure: T) -> ServiceFn<T2>
    where T: Fn(http::Request<Body>) -> F + Send + Sync,
    T2: Fn(lambda_http::Request) -> F2,
        F2: Future<Output=crate::Result<http::Response<Body>>>,
          F: Future<Output=crate::Result<I>> + Send {
    let outer_closure = move |event: ApiProxyRequest| async move {
        match handler_func_closure(event).await {
            Ok(s) => Ok(s.into_response()),
            Err(e) => failure(Box::new(e), None),
        }
    };

    service_fn(outer_closure)
};

I have seen other question asked on how can function return closure, but I cannot seem to get it to work in this case. I am curious to know if anyone able to get this to work where an outer closure returned by a function can be passed in to service_fn.

I feel like I've spent countless hours in trying to figure this out, but I've been unable to make it work. I'm not entirely sure whether this is possible in Rust to be honest, but I'd be curious if anyone has an idea how to approach this.


Edit: This is the compiler error I'm getting in case it's helpful:

error[E0308]: mismatched types
   --> src/bin/my_lambda/main.rs:197:16
    |
185 |    pub fn get_service_fn<T, T2, F, F2, I: IntoResponse>(handler_func_closure: T) -> ServiceFn<T2>
    |                             -- this type parameter
...
190 |        let outer_closure = move |event: ApiProxyRequest| async move {
    |  __________________________-________________________________________-
    | | _________________________|
    | ||
191 | ||         match handler_func_closure(event).await {
192 | ||             Ok(s) => Ok(s.into_response()),
193 | ||             Err(e) => failure(Box::new(e), None),
194 | ||         }
195 | ||     };
    | ||     -
    | ||_____|
    | |______the found closure
    |        the found `async` block
196 | 
197 |        service_fn(outer_closure)
    |                   ^^^^^^^^^^^^^ expected type parameter `T2`, found closure
    |
   ::: /Users/rnag/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/future/mod.rs:61:43
    |
61  |    pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return>
    |                                              ------------------------------- the found opaque type
    |
    = note: expected type parameter `T2`
                      found closure `[closure@src/bin/my_lambda/main.rs:190:25: 195:6]`
    = help: every closure has a distinct type and so could not always match the caller-chosen type of parameter `T2`
fedonev
  • 20,327
  • 2
  • 25
  • 34
rv.kvetch
  • 9,940
  • 3
  • 24
  • 53
  • It would be helpful is you added the error messages you are getting from the compiler. – rodrigo Mar 14 '22 at 16:54
  • The compiler error doesn't make much sense to me, but yeah I can include it above in the question. not sure it's going to be much help though. – rv.kvetch Mar 14 '22 at 16:58
  • Indeed it helps. It would be better a minimum compilable example, but... maybe you can try removing the `T2` generic argument and returning `ServiceFn F2>`. – rodrigo Mar 14 '22 at 18:01
  • @rodrigo That won't work as the caller can't possibly know the type to supply for `F2`, it's a compiler-generated type, just like the closure type. – cdhowie Mar 14 '22 at 18:02
  • Does this answer your question? [Return an async function from a function in Rust](https://stackoverflow.com/questions/61167939/return-an-async-function-from-a-function-in-rust) – cdhowie Mar 14 '22 at 18:02
  • @cdhowie thanks for the suggestion, but It's not working. I've tried `Box::pin` and a hundred different variations, but no dice. It seems like this is a frustrating limitation of the Rust language in general, because I literally *cannot* get a function to return a closure that contains another closure in my case. – rv.kvetch Mar 25 '22 at 13:04
  • 1
    @rv.kvetch You might want to ask another question or add on to this question with the things you've tried. – cdhowie Mar 25 '22 at 14:18
  • @cdhowie done! I created a new question [here](https://stackoverflow.com/questions/71619547/how-to-create-a-generic-function-to-wrap-an-async-closure) with an MRE of the issue i'm facing, more or less. – rv.kvetch Mar 25 '22 at 15:47

0 Answers0