Home > Mobile >  Rust struct to lambda_http
Rust struct to lambda_http

Time:11-07

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.

CodePudding user response:

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.

  • Related