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.