Context
I'm using actix-web
and sqlx
to create a simple HTTP server with basic auth service.
Services
I have several services that look like this
#[derive(Clone)]
pub struct AuthFetchService<'service> {
fetch_store: &'service (dyn Fetch<Auth, Arg = Arg> + Send + Sync)
}
impl<'service> AuthFetchService<'service> {
pub async fn fetch(&self, arg: &Arg) -> ServiceResult<Auth> {
self.fetch_store
.fetch(arg)
.await
.map_err(ServiceError::from)
}
}
where each ___store
(here fetch_store
) is a trait object that implements some sort of fetch
function, which is just an SQL query result (sqlx
) given some database pool that implements Send + Sync
. Trait because I want to make it DB agnostic.
Aggregates
These services together perform an "aggregate" task, described as
#[derive(Clone)]
pub struct EntityAggregate<'entity> {
auth_fetch: &'entity AuthFetchService<'entity>,
token_create: &'entity TokenCreateService<'entity>,
entity_fetch: &'entity EntityFetchService<'entity>,
...
...
}
impl<'entity> EntityAggregate<'entity> {
pub async fn fetch(&self, token: String, arg: Arg) -> AggregateResult<Entity> {
// Check token's validity.
// Make sure token still exists in db.
// Make sure token has enough privileges to 'fetch' this entity.
// Make sure requested entity exists.
// etc.
}
}
The idea is for services
to do one single task and aggregates
to basically orchestrate different services to complete a full task given by the user.
Main
Concretely constructed as
let store = Postgres::new()
.await
.expect("Failed to initialize `Postgres` store");
let auth_fetch = AuthFetchService::new(&store);
let token_create = TokenCreateService::new(&store);
let entity_fetch = EntityFetchService::new(&store);
...
...
let entity_aggregate = Arc::new(EntityAggregate::new(
&auth_fetch,
&token_create,
&entity_fetch,
...
...
);
HttpServer::new(move || {
App::new()
.app_data(web::JsonConfig::default().limit(4096))
.app_data(Data::from(entity_aggregate.clone()))
.service(
web::scope("/api/v1")
.service(route::entity::fetch)
)
})
.bind(("0.0.0.0", 8080))?
.run()
.await
Problem
--> src/main.rs:66:44
|
66 |let auth_fetch = AuthFetchService::new(&store);
| ^^^^^^
| |
| borrowed value does not live long enough
| cast requires that `store` is borrowed for `'static`
...
192 | }
| - `store` dropped here while still borrowed
error[E0597]: `auth_fetch` does not live long enough
--> src/main.rs:98:9
|
98 | &auth_fetch,
| ^^^^^^^^^^^ borrowed value does not live long enough
...
143 | / App::new()
144 | | .wrap(TracingLogger::default())
... |
163 | | .app_data(Data::from(entity_aggregate.clone()))
| |___________________________________________________________
argument requires that `auth_fetch` is borrowed for `'static`
...
192 | }
| - `auth_fetch` dropped here while still borrowed
(line 192 is end of main fn)
I understand app_data
requires 'static
lifetime which I thought maybe putting aggregrator in Arc
and then cloning inside the closure might satisfy. But sadly it doesn't.
So my question is, would it be possible to make this setup work without wrapping references in Rc
or perhaps Arc
and generous use of .clone()