How to alias in Rust without explicit lifetimes?

Viewed 100

I need a way to make important data read-accessible, thread-safe throughout my application.

My web application in Rust with Actix-Web. Permission data is prefetched when the application starts. To be useful, the request cycle must be able to access this data and test if the current user.

Example:

#[get("/forums/{forum}/")]
pub async fn view_forum(client: ClientCtx) -> Result<impl Responder, Error> {
    if !client.can_view_forum() {
        // return error
    }
    // ...

The permission data is made available to the Actix-Web web server as app_data, so this might work.

#[get("/forums/{forum}/")]
pub async fn view_forum(
    client: ClientCtx,
    permissions: web::Data<PermissionData>,
) -> Result<impl Responder, Error> {
    if !permissions.can_view_forum(client) {
        // return error
    }
    // ...

I would like to avoid increasing the size of my route function signatures as much as possible. I will need permissions accessible everywhere the user is also accessible.

PermissionData is quite large and ClientCtx's user data is pulled on every request. Refetching data directly to the client every request is not practical.

Actix-Web's middleware system creates these structs during the request cycle. It works in such a way that lifetimes cannot be attached to the ClientUser.

pub struct ClientCtxInner<'a> {
    pub client: Option<ClientUser>,
    pub permission: &PermissionData, // Doesn't work.
    pub request_start: Instant,
}

In short:

  1. The user never needs to write to permissions. It just needs to read them.
  2. Many users on all threads at all times will be reading this same data.
  3. I can't use normal lifetimes.
  4. I would very much prefer if the data is accessible directly through the Client struct for organization reasons.

This is where the middleware assembles the client.

    fn call(&self, req: ServiceRequest) -> Self::Future {
        let (httpreq, payload) = req.into_parts();
        let cookies = Session::extract(&httpreq).into_inner();
        let req = ServiceRequest::from_parts(httpreq, payload);
        let permissions: Option<&PermissionData> = req.app_data::<PermissionData>(); // My data!
        let ctx = ClientCtx::get_client_ctx(&mut *req.extensions_mut());
        let fut = self.service.call(req);

        async move {
            match cookies {
                Ok(cookies) => {
                    let result = authenticate_client_ctx(&cookies).await;

                    // Data needs to be attached here.
                    // ctx.0.borrow_mut().permissions = ???;

                    // Assign the user to our ClientCtx struct.
                    if let Some(user) = result {
                        ctx.0.borrow_mut().client = Some(user);
                    }
                }
                Err(e) => {
                    log::error!("ClientCtxMiddleware: Session::extract(): {}", e);
                }
            };
            let result = fut.await?;
            Ok(result)
        }
        .boxed_local()
    }
0 Answers
Related