Home > Blockchain >  Rust Actix Web use Hashmap in both middleware and app data lifetime error
Rust Actix Web use Hashmap in both middleware and app data lifetime error

Time:08-27

I was trying to have a Hashmap that can be used in both app data and in the middleware, but I am getting lifetime errors.

I can do that with Atomics but there is no Atomic for hashmap, and a hashmap cannot be static, how do I make sure that it lives longer than the big chunky function below?

#[derive(Clone, Debug)]
struct HashMapContainer(pub Arc<Mutex<HashMap<String, u32>>>);

#[actix_web::main]
async fn main() -> io::Result<()> {
    // Here's what I want to do, but with a hashmap instead
    static COUNTER: AtomicU32 = AtomicU32::new(1);

    let hashmap = HashMapContainer(Arc::new(Mutex::new(HashMap::new())));

    HttpServer::new(move || {
        App::new()
            // Middleware function called every time when there is a request no matter what is the path
            .wrap_fn(|req, srv| {
                // increase counter by 1
                COUNTER.store(COUNTER.load(Relaxed)   1, Relaxed);

                // So I can do stuff with the hashmap (should be mutable)
                dbg!(&hashmap.0.lock().unwrap());
                srv.call(req)
            })
            // I also want to use those 2 variables as app data
            .app_data(web::Data::new(SessionStats::new()))
            .app_data(web::Data::new(hashmap.clone()))
    })
    .bind(("0.0.0.0", 8080))?
    .run()
    .await?;

    Ok(())
}

Error:

error: lifetime may not live long enough
  --> src/main.rs:17:9
   |
16 |       HttpServer::new(move || {
   |                       ------- lifetime `'1` represents this closure's body
17 | /         App::new()
19 | |             .wrap_fn(|req, srv| {
...  |
23 | |                 srv.call(req)
24 | |             })
   | |______________^ argument requires that `'1` must outlive `'static`
   |
   = note: closure implements `Fn`, so references to captured variables can't escape the closure

CodePudding user response:

Your approach of using an Arc<Mutex> is 100% correct.

There are just some detail problems with your code.

All the problems you see come from ownership. The closures all have to be move to own a Arc object. But then they consume the outer one, so you have to clone it beforehand. That makes the code a little ugly, but well, that's how it is.

Here you go:

use std::{
    collections::HashMap,
    io,
    sync::{
        atomic::{AtomicU32, Ordering::Relaxed},
        Arc, Mutex,
    },
};

use actix_web::{dev::Service, web, App, HttpServer};

struct SessionStats;

impl SessionStats {
    pub fn new() -> Self {
        Self
    }
}

#[derive(Clone, Debug)]
struct HashMapContainer(pub Arc<Mutex<HashMap<String, u32>>>);

#[actix_web::main]
async fn main() -> io::Result<()> {
    // Here's what I want to do, but with a hashmap instead
    static COUNTER: AtomicU32 = AtomicU32::new(1);

    let hashmap = HashMapContainer(Arc::new(Mutex::new(HashMap::new())));
    // Clone here, this one will be owned by the first closure
    let hashmap_for_httpserver = hashmap.clone();

    HttpServer::new(move || {
        // Clone the hashmap owned by the closure.
        // The one that the closure owns is only borrowable, but we want to have an owned one so we can
        // move it into the next closure
        let hashmap_for_app = hashmap_for_httpserver.clone();

        App::new()
            // Middleware function called every time when there is a request no matter what is the path
            .wrap_fn(move |req, srv| {
                // increase counter by 1
                COUNTER.store(COUNTER.load(Relaxed)   1, Relaxed);

                // So I can do stuff with the hashmap (should be mutable)
                dbg!(&hashmap_for_app.0.lock().unwrap());
                srv.call(req)
            })
            // I also want to use those 2 variables as app data
            .app_data(web::Data::new(SessionStats::new()))
            .app_data(web::Data::new(hashmap_for_httpserver.clone()))
    })
    .bind(("0.0.0.0", 8080))?
    .run()
    .await?;

    Ok(())
}
  • Related