How to pass data from request to response without copying in actix-web

I have a User structure that is deserialized from the request body. I want to fill the data of the User structure (field name) according to the data received from the database and return the response.

  1. Without copying, borrow the username from the database data, which is stored in the global structure
  2. Return a response of the same User structure with a filled-in name

I use tokio::task::spawn_blocking because there are blocking operations in the real project

lazy_static! {
    pub static ref USER_DATA: Mutex<Arc<HashMap<String, String>>> = Mutex::new(Arc::new(Default::default()));
}

#[derive(Deserialize, Serialize, Default)]
struct User<'a> {
    id: &'a str,

    #[serde(skip_deserializing)]
    name: &'a str
}

struct Response<'a> {
    user: web::Json<User<'a>>,
    common_data: Arc<HashMap<String, String>>
}

impl Response<'_> {
    pub fn fill_user(mut self) -> Self {
        self.user.name = self.common_data.get(self.user.id).unwrap();
        self
    }
}

impl Responder for Response<'_> {
    fn respond_to(self, req: &HttpRequest) -> HttpResponse {
        HttpResponse::build(StatusCode::OK).body(serde_json::to_string(&self.user).unwrap())
    }
}

async fn calculate(req: HttpRequest, user: web::Json<User<'static>>) -> impl Responder {

    let mut resp = Response {
        user,
        common_data: USER_DATA.lock().await.clone()
    };

    tokio::task::spawn_blocking(move || {
        resp.fill_user()
    }).await.unwrap()

}

Now I am getting this error

error: implementation of `_::_serde::Deserialize` is not general enough
  --> src\main.rs:62:46
   |
62 |             .route("/calculate", web::post().to(calculate))
   |                                              ^^ implementation of `_::_serde::Deserialize` is not general enough
   |
   = note: `User<'static>` must implement `_::_serde::Deserialize<'0>`, for any lifetime `'0`...
   = note: ...but `User<'_>` actually implements `_::_serde::Deserialize<'1>`, for some specific lifetime `'1`

And sometimes i have an error: "returns a value referencing data owned by the current function"

The web server is trying to deserialize non-static data (the request) and obtain an User<'static>. This is not possible. Give up on using &str in this manner. It's not going to work.

Try something like this. It avoids copying the string in the user data by storing it in the shared type Arc<str>. Cloning an Arc<str> is extremely cheap due to its shared nature.

lazy_static! {
    pub static ref USER_DATA: Mutex<Arc<HashMap<String, Arc<str>>>> = Mutex::new(Arc::new(Default::default()));
}

#[derive(Deserialize, Default)]
struct UserRequest {
    id: String,
}
#[derive(Serialize, Default)]
struct UserResponse {
    id: String,
    name: Option<Arc<str>>,
}

struct Response {
    user: web::Json<UserResponse>,
    common_data: Arc<HashMap<String, Arc<str>>>
}

impl Response {
    pub fn fill_user(mut self) -> Self {
        self.user.name = Some(self.common_data.get(&self.user.id).unwrap().clone());
        self
    }
}

impl Responder for Response {
    fn respond_to(self, req: &HttpRequest) -> HttpResponse {
        HttpResponse::build(StatusCode::OK).body(serde_json::to_string(&self.user).unwrap())
    }
}

async fn calculate(req: HttpRequest, user: web::Json<User<'static>>) -> impl Responder {

    let mut resp = Response {
        user,
        common_data: USER_DATA.lock().await.clone()
    };

    tokio::task::spawn_blocking(move || {
        resp.fill_user()
    }).await.unwrap()
}

Note that you need to enable a feature to serialize an Arc in this manner.

Note also that the Mutex<Arc<...>> combination is probably not what you wanted here.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.