AWS lambda rust - how to make a function to return outer closure? - amazon-web-services

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`

Related

Getting error "expected function" when trying to call function from a unit test

I'm trying to write a simple unit test case for an Actix web function, however I'm getting an error when trying to call the function from the test function. The error I'm getting is: E0618: expected function, found <my function name>
I've tried calling it exactly the way advised on Actix website.
Here's a code sample:
use actix_web::{get, web, Responder, Result};
use serde::Serialize;
#[derive(Serialize, Debug)]
struct PingResponse {
status: String,
}
#[get("/ping")]
async fn health_check() -> Result<impl Responder> {
//web::Json<PingResponse> {
let resp = PingResponse {
status: "alive".to_string(),
};
Ok(web::Json(resp))
}
#[cfg(test)]
mod tests {
use super::*;
use actix_web::test;
#[actix_web::test]
async fn test_ping_ok() {
let req = test::TestRequest::default().to_http_request();
// E0618 expected function, found `health::health_check`
let resp = health_check(req).await;
// E0618: expected function, found `health_check`
// let resp = crate::apis::health::health_check(req);
assert_eq!(resp.status(), "alive".to_string());
}
}
I've tried calling the health_check function by just using the function name as well as by using the fully qualified function name.
The diagnostic message is:
error[E0618]: expected function, found `apis::health::health_check`
--> src/apis/health.rs:29:20
|
9 | #[get("/ping")]
| --------------- `apis::health::health_check` defined here
...
29 | let resp = health_check(req).await;
| ^^^^^^^^^^^^-----
| |
| call expression requires function
I can see that the function isn't returning what I think it was, but rather, a struct. I have found a couple solutions:
Remove the #[get("/ping")] attribute and do the routing from the http server setup. This allows me to call the function normally from the unit test.
Use test::TestRequest::get() and then do app.call(req) to make the call generically. This way I can leave the routing on the function.

Rust struct to lambda_http

I'm trying to use lambda_http but having an issue with it.
Basically, I'm trying to create a service_fn by passing a Service trait implemented object to it for a structure that holds state. But it seems to be tripping on std::marker::Send
struct Handler {
db: aws_sdk_dynamodb::Client,
s3: aws_sdk_s3::Client,
}
impl Handler {
..some_stuff..
}
pub trait Service {
fn route<T>(&self, data: T) -> serde_json::Value;
}
impl Service for Handler {
fn route<Request>(&self, req: Request) -> serde_json::Value {
json!({ "error": "Route not found" })
}
}
pub async fn run(service: Arc<impl Service>) -> Result<(), Error> {
let func = service_fn(|req| lambda_handler(req, service.clone()));
lambda_http::run(func).await?;
Ok(())
}
pub(crate) async fn lambda_handler(
evt: Request,
svc: Arc<impl Service>,
) -> Result<impl IntoResponse, Error> {
let reply = svc.route(evt);
let response = LambdaResponse::builder().(..some_stuff..).body(reply.to_string()).unwrap();
Ok(response)
}
For some reason this doesnt quite work and its tied to synchronization but not sure how to fix it.
Compiling runtime v0.1.0 (/home/xyz/Documents/lservice/runtime)
warning: unused import: `serde_json::json`
--> runtime/src/lib.rs:12:5
|
12 | use serde_json::json;
| ^^^^^^^^^^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default
error: future cannot be sent between threads safely
--> runtime/src/lib.rs:21:22
|
21 | lambda_http::run(func).await?;
| ^^^^ future returned by `lambda_handler` is not `Send`
|
note: captured value is not `Send`
--> runtime/src/lib.rs:29:5
|
29 | svc: Arc<impl Service>,
| ^^^ has type `Arc<impl Service>` which is not `Send`
note: required by a bound in `lambda_http::run`
--> /home/xyz/.cargo/registry/src/github.com-1ecc6299db9ec823/lambda_http-0.7.1/src/lib.rs:185:16
|
185 | S::Future: Send + 'a,
| ^^^^ required by this bound in `lambda_http::run`
help: consider further restricting this bound
|
19 | pub async fn run(service: Arc<impl Service + std::marker::Sync>) -> Result<(), Error> {
| +++++++++++++++++++
error: future cannot be sent between threads safely
--> runtime/src/lib.rs:21:22
|
21 | lambda_http::run(func).await?;
| ^^^^ future returned by `lambda_handler` is not `Send`
|
note: captured value is not `Send`
--> runtime/src/lib.rs:29:5
|
29 | svc: Arc<impl Service>,
| ^^^ has type `Arc<impl Service>` which is not `Send`
note: required by a bound in `lambda_http::run`
--> /home/xyz/.cargo/registry/src/github.com-1ecc6299db9ec823/lambda_http-0.7.1/src/lib.rs:185:16
|
185 | S::Future: Send + 'a,
| ^^^^ required by this bound in `lambda_http::run`
help: consider further restricting this bound
|
19 | pub async fn run(service: Arc<impl Service + std::marker::Send>) -> Result<(), Error> {
| +++++++++++++++++++
warning: `runtime` (lib) generated 1 warning
error: could not compile `runtime` due to 2 previous errors; 1 warning emitted
This does however work if I just pass dynamodb.clone() to it directly rather than nested inside the Handler struct so I know it probably needs me to implement Send on Handler, but not sure if I'm on the right track here so need a little help on the way forward.
<# UPDATE #>
One thing that made this eventually work, was by just protecting the Handler with a Arc<Mutex>. Since it was complaining about a value not being able to pass between threads safely, I just locked it before sending it. Though I can't shake the feeling that this is a sledgehammer approach, especially given if I use lambda_runtime::run (instead of lambda_http::run) this seems to work with just a simple Arc::clone().
<# UPDATE 2 #>
A suggestion by #Mikdore to also implement Sync as a supertrait did the trick. Now it doesnt need any external locks and just works as is.
You don't have to implement Send. And you almost always probably shouldn't. If all members of your struct are Send, rust will implement it for you automatically. The compiler tells you what the problem is:
help: consider further restricting this bound
|
19 | pub async fn run(service: Arc<impl Service + std::marker::Send>) -> Result<(), Error> {
| +++++++++++++++++++
Any arbitrary type could in theory implement Service. Not all of those types are Send. You can, as the compiler tells you, restrict your function to take Arc<impl Service + Send>. However, perhaps more sensibly, you can also make Service a supertrait of Send, by doing:
trait Service: Send {
//...
}
This way, every type implementing Service is required to implement Send, and you can use impl Service in async contexts.

Problems using `u8` in Substrate and ink

I am trying to add a simple u8 to my Substrate Runtime Module:
decl_storage! {
trait Store for Module<T: Trait> as TemplateModule {
MyByte: u8;
}
}
However, I get a compiler error that it does not implement Parity Codec's Encode or Decode:
error[E0277]: the trait bound `u8: _IMPL_DECODE_FOR_Event::_parity_codec::Encode` is not satisfied
--> /Users/shawntabrizi/Documents/GitHub/substrate-package/substrate-node-template/runtime/src/template.rs:23:1
|
23 | / decl_storage! {
24 | | trait Store for Module<T: Trait> as TemplateModule {
25 | | MyByte: u8;
26 | | }
27 | | }
| |_^ the trait `_IMPL_DECODE_FOR_Event::_parity_codec::Encode` is not implemented for `u8`
A similar problem occurs when I try to store a u8 in a Substrate Smart Contract using ink!:
contract! {
struct MyContract {
value: storage::Value<u8>,
}
...
}
Error:
error[E0277]: the trait bound `u8: parity_codec::codec::Encode` is not satisfied
--> src/lib.rs:26:1
|
26 | / contract! {
27 | | struct MyContract {
28 | | value: storage::Value<u8>,
29 | | }
... |
49 | | }
50 | | }
| |_^ the trait `parity_codec::codec::Encode` is not implemented for `u8`
Why is that, and what can I do to resolve the issue?
Today, the parity_codec does not support Encoding of u8 due to avoid a type collision, since Vec<u8> is a special case from Vec<T>.
See: https://github.com/paritytech/parity-codec/issues/47
gavofyork:
Because it would otherwise make the two encodings: Vec<u8> and Vec<T: Codec> clash.
It is possible this could be fixed in the future with additional Rust features, but for now, you will need to store your single bytes as [u8; 1] and work with that type.
Substrate Runtime Module
One hacky solution for a Substrate Runtime Module looks something like this:
use support::{decl_module, decl_storage, decl_event, StorageValue, dispatch::Result};
use system::ensure_signed;
pub trait Trait: system::Trait {
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
}
type U8 = [u8; 1];
decl_storage! {
trait Store for Module<T: Trait> as TemplateModule {
MyByte get(my_byte): U8;
}
}
decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
fn deposit_event<T>() = default;
pub fn set_my_byte(origin, input: U8) -> Result {
let who = ensure_signed(origin)?;
<MyByte<T>>::put(input);
Self::deposit_event(RawEvent::MyByteStored(input, who));
Ok(())
}
pub fn add_to_byte(origin, input: U8) -> Result {
let who = ensure_signed(origin)?;
let my_byte = Self::my_byte()[0];
let my_new_byte = my_byte.checked_add(input[0]).ok_or("Overflow")?;
<MyByte<T>>::put([my_new_byte]);
Self::deposit_event(RawEvent::MyByteStored([my_new_byte], who));
Ok(())
}
}
}
decl_event!(
pub enum Event<T> where AccountId = <T as system::Trait>::AccountId {
MyByteStored(U8, AccountId),
}
);
Where we assign a new type type U8 = [u8; 1];. The choice of our new type name is important since it will trick the Polkadot UI to treat this value simply as a u8 for any input/output fields it generates. If you try to use a custom type like type Byte = [u8; 1], the UI will ask you to import the definition of that custom type. If you try to use [u8; 1] directly, the Polkadot UI will not know how to render the input/output of that value.
Furthermore, as of the time of writing this post, the decl_event! macro has an issue depositing a [u8; 1] directly due to pattern matching.
Note that you will need to treat this type like an array when you use it. add_to_byte() shows an example of that. So ultimately, you need to extract the first item of the array to extract the byte, and you need to wrap your byte in an array to set a U8:
let my_byte = Self::my_byte()[0];
...
<MyByte<T>>::put([my_new_byte]);
Other solutions may involve using other types which are natively supported like Vec<u8> or u16, and doing the appropriate checks on your runtime that this is treated like a single u8, but the UI will not know better.
Substrate Smart Contracts
I have not found a great solution for ink! yet, but you should be able to use [u8; 1] directly in all of your code. Again, you will need to treat it like an array for getters and setters. But when generating the ABI you will need to manually change the instances of [u8; 1] to u8 to trick the UI to do what you want.

Can't move out of borrowed content using unwrap in if statement [duplicate]

This is the code I am trying to execute:
fn my_fn(arg1: &Option<Box<i32>>) -> i32 {
if arg1.is_none() {
return 0;
}
let integer = arg1.unwrap();
*integer
}
fn main() {
let integer = 42;
my_fn(&Some(Box::new(integer)));
}
(on the Rust playground)
I get the following error in previous versions of Rust:
error[E0507]: cannot move out of borrowed content
--> src/main.rs:5:19
|
5 | let integer = arg1.unwrap();
| ^^^^ cannot move out of borrowed content
And in more modern versions:
error[E0507]: cannot move out of `*arg1` which is behind a shared reference
--> src/main.rs:5:19
|
5 | let integer = arg1.unwrap();
| ^^^^
| |
| move occurs because `*arg1` has type `std::option::Option<std::boxed::Box<i32>>`, which does not implement the `Copy` trait
| help: consider borrowing the `Option`'s content: `arg1.as_ref()`
I see there is already a lot of documentation about borrow checker issues, but after reading it, I still can't figure out the problem.
Why is this an error and how do I solve it?
Option::unwrap() consumes the option, that is, it accepts the option by value. However, you don't have a value, you only have a reference to it. That's what the error is about.
Your code should idiomatically be written like this:
fn my_fn(arg1: &Option<Box<i32>>) -> i32 {
match arg1 {
Some(b) => **b,
None => 0,
}
}
fn main() {
let integer = 42;
my_fn(&Some(Box::new(integer)));
}
(on the Rust playground)
Or you can use Option combinators like Option::as_ref or Option::as_mut paired with Option::map_or, as Shepmaster has suggested:
fn my_fn(arg1: &Option<Box<i32>>) -> i32 {
arg1.as_ref().map_or(0, |n| **n)
}
This code uses the fact that i32 is automatically copyable. If the type inside the Box weren't Copy, then you wouldn't be able to obtain the inner value by value at all - you would only be able to clone it or to return a reference, for example, like here:
fn my_fn2(arg1: &Option<Box<i32>>) -> &i32 {
arg1.as_ref().map_or(&0, |n| n)
}
Since you only have an immutable reference to the option, you can only return an immutable reference to its contents. Rust is smart enough to promote the literal 0 into a static value to keep in order to be able to return it in case of absence of the input value.
Since Rust 1.40 there is Option::as_deref, so now you can do:
fn my_fn(arg1: &Option<Box<i32>>) -> i32 {
*arg1.as_deref().unwrap_or(&0)
}

How handling a list of polymorphic variants?

Let two variant types :
type typeA =
| A1
| A2
;;
type typeB =
| B1 of typeA
| B2 of typeA
;;
and type-checking functions :
let isA1 = function A1 -> true | _ -> false;;
let isA2 = function A2 -> true | _ -> false;;
let isB1 = function B1 e -> true | _ -> false;;
let isB2 = function B2 e -> true | _ -> false;;
I'd like to create a list of those functions to check elements of type A or B
as they're of different types, I need polymorphic variants and I get :
type filterA =
{
handleA : typeA -> bool;
};;
type filterB =
{
handleB : typeB -> bool;
};;
type filterslist = [`FilterA of filterA | `FilterB of filterB] list ;;
let filters1 = [`FilterA { handleA = isA1 }; `FilterB { handleB = isB1 }] ;;
So now I want to iterate over filters1 to check the type of the argument
I tried :
let exec_filters filters event = List.iter (fun fil -> match fil with `FilterA -> fil.handleA event; ()| `FilterB -> fil.handleB event; () ) filters;;
but it's not appreciated :
Error: This expression has type [< `FilterA | `FilterB ]
but an expression was expected of type filterA
How can I handle this ?
The fact that you're using "type checking predicates" similar to Scheme or instanceOf indicates that there is probably something very wrong with your code. OCaml is a statically typed language, you should not:
iterate over filters1 to check the type of the argument I tried
Why are you doing this? If you are trying to handle multiple types, the way to do it is to use polymorphism. Polymorphic variants can be helpful for this, but I'm still not convinced that your code isn't just written in a strange way.
I think your code should read like:
let exec_filters filters event =
List.iter
(fun fil -> match fil with
| `FilterA fA -> fA.handleA event; ()
| `FilterB fB -> fB.handleB event; () )
filters;;
EDIT: However, this won't typecheck, since event can't have types typeA and typeB...
Why not make your initial variants (typeA and typeB) polymorphic?
What are you trying to do?
When you say
match fil with
`FilterA -> ...
You seem to expect that this will change the type of fil, but that's not how it works. The expression with the type filterA appears inside the pattern. You want something more like this:
match fil with
`FilterA { handleA = h } -> h event
I'm not sure I see the purpose of having your handlers return bool if you're going to use List.iter to execute them. This will return unit, and the bool values are going to be discarded.
Edit
There's a deeper typing problem, explained well by Ptival. So even if you fix your patterns you'll still need to rethink your plan. One possible thing to do would be to use variants (not necessarily polymorphic variants, by the way) to track the types of the events.